<?php
// +----------------------------------------------------------------------
// | Note动态服务服务层
// +----------------------------------------------------------------------
// | 主要服务于Note动态数据处理
// +----------------------------------------------------------------------
// | 错误编码头 212xxx
// +----------------------------------------------------------------------
namespace common\service;

use common\exception\LogicException;
use common\model\CosMediaModel;
use common\model\EntityModel;
use common\model\MediaModel;
use common\model\NoteEntityRelationModel;
use common\model\NoteModel;
use common\model\OrderTaskListModel;
use common\model\PlanModel;
use common\model\ProjectMemberModel;
use common\model\ProjectModel;
use common\model\RelationNoteEntityRelationModel;
use common\model\ReviewRequestModel;
use common\model\ReviewRequestNodeModel;
use common\model\StatusModel;
use common\model\StepCategoryModel;
use common\model\TaskModel;
use common\model\TaskRepairOrderModel;
use common\model\UserModel;
use common\model\VersionCheckListModel;
use common\model\VersionModel;
use support\EntityTaskFilterParser;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\Hook;
use think\module\Module;
use Throwable;
use Webman\Stomp\Client;
use function request;

class NoteService
{
    // 单例工具
    use SingletonTrait;

    use EntityTaskFilterParser;

    public function __construct()
    {
    }

    /**
     * 创建note
     * @param $data
     * @param bool $resultWanted
     * @param bool $isImport 是否从导入入口调用
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function createNote($data, $resultWanted = true, $isImport = false)
    {
        $noteModel = model(NoteModel::class);
        $noteModel->setCurrentModuleCode('note');


        $mediaModel = model(MediaModel::class);
        $cosMediaModel = model(CosMediaModel::class);
        $mediaMap = ["media" => $mediaModel, "cos_media" => $cosMediaModel];

        $mediaService = MediaService::getInstance();
        $noteModule = module_by_code('note');

        $demandType = $data['note']["demand_type"] ?? "";

        $noteEntityRelationList = [];
        $entityIds = [];
        $episodeCodes = [];
        $projectId = [];

        $demandTypeList = [];
        if (!empty($data["note_entity_relation"])) {
            //暂时限制为100个
            $limit = 100;
            if (count($data["note_entity_relation"]) > $limit) {
                throw new LogicException('note entity relation sum exceed the limit', ErrorCode::NOTE_ENTITY_RELATION_SUM_EXCEED_THE_LIMIT);
            }
            //首先去重
            //如果状态为审核反馈,需要判定是否有版本
            foreach ($data["note_entity_relation"] as $noteEntityRelationItem) {
                $noteEntityRelationKey = join("_", $noteEntityRelationItem);
                $noteEntityRelationList[$noteEntityRelationKey] = $noteEntityRelationItem;
            }
            $noteEntityRelationList = array_values($noteEntityRelationList);

            $taskIds = [];
            foreach ($noteEntityRelationList as $noteEntityRelationListItem) {
                if ($noteEntityRelationListItem['relation_type'] === NoteEntityRelationModel::RELATION_TYPE_TODO) {
                    continue;
                }
                if (empty($projectId)) {
                    $projectId = $noteEntityRelationListItem["project_id"];
                }
                if ($noteEntityRelationListItem["relation_type"] === "episode") {
                    $episodeCodes[$noteEntityRelationListItem["episode_code"]] = $noteEntityRelationListItem["episode_code"];
                } else {
                    $entityIds[$noteEntityRelationListItem["entity_id"]] = $noteEntityRelationListItem["entity_id"];

                    if ($noteEntityRelationListItem["relation_type"] === "step") {
                        $taskIds[] = $noteEntityRelationListItem["task_id"];
                    }
                }
            }
            $taskIds = array_values($taskIds);

            $taskList = [];
            if (!empty($taskIds)) {
                $taskList = model(TaskModel::class)->where(['id' => ["IN", $taskIds]])->field("id,step_id,step_category_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status")->select();
                $taskList = array_column($taskList, null, "id");
            }

            $entityFilter = [];
            if (!empty($entityIds)) {
                $entityIds = array_values($entityIds);
                $entityFilter = [
                    "entity.id" => ["IN", $entityIds]
                ];
            }

            if (!empty($episodeCodes)) {
                $episodeCodes = array_values($episodeCodes);
                if (empty($entityFilter)) {
                    $entityFilter = ["entity.code" => ["IN", $episodeCodes], "entity.project_id" => $projectId];
                } else {
                    $entityFilter = [
                        $entityFilter,
                        ["entity.code" => ["IN", $episodeCodes], "entity.project_id" => $projectId],
                        "_logic" => "OR"
                    ];
                }
            }

            $noteCurrentModuleCode = $data['note']["module_code"] ?? "";
            $demandTypeList = [];
            // 查找entity_id 将资产id替换概念id , 将 镜头id 替换为分镜id
            if (!empty($entityFilter)) {
                $entityList = model(EntityModel::class)
                    ->join("module on entity.module_id = module.id")
                    ->where($entityFilter)
                    ->field(["entity.id", "entity.module_id", "entity.code", "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_design'))) as design",
                        "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.shot_storyboard')) as shot_storyboard",])
                    ->select();

                $entityUpEntityIdMap = [];

                $entityIds = array_map(function ($resultItem) use (&$entityUpEntityIdMap, &$noteCurrentModuleCode) {
                    // 判定当前的模块
                    if (empty($noteCurrentModuleCode)) {
                        if (in_array($resultItem["module_id"], [module_by_code("asset")["id"], module_by_code("level")["id"], module_by_code("design")["id"]])) {
                            $noteCurrentModuleCode = "asset";
                        } else {
                            $noteCurrentModuleCode = "shot";
                        }
                    }
                    if (in_array($resultItem["module_id"], [module_by_code("asset")["id"], module_by_code("level")["id"]])) {
                        $entityUpEntityIdMap[$resultItem["id"]] = intval($resultItem["design"]);
                        return intval($resultItem["design"]);
                    } elseif ($resultItem["module_id"] === module_by_code("shot")["id"]) {
                        $entityUpEntityIdMap[$resultItem["id"]] = intval($resultItem["shot_storyboard"]);
                        return intval($resultItem["shot_storyboard"]);
                    } else {
                        return $resultItem["id"];
                    }
                }, $entityList);

                // 任务没有开始过的
                $taskNotStartStartedStatusList = StatusService::getInstance()->getNotStartedStatus();
                $taskNotStartStartedStatusList = array_column($taskNotStartStartedStatusList, 'id');
                $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
                $taskNotStartStartedStatusList = array_merge($taskNotStartStartedStatusList, [$closedStatus['id']]);

                $noteEntityRelationList = array_map(function ($noteEntityRelationItem) use ($entityUpEntityIdMap, $noteCurrentModuleCode, $data, &$demandTypeList, $taskList, $taskNotStartStartedStatusList) {
                    if (isset($entityUpEntityIdMap[$noteEntityRelationItem["entity_id"]])) {
                        $noteEntityRelationItem["entity_id"] = $entityUpEntityIdMap[$noteEntityRelationItem["entity_id"]];
                    }
                    if (empty($noteEntityRelationItem["relate_module_code"])) {
                        $noteEntityRelationItem["relate_module_code"] = $noteCurrentModuleCode;
                    }
                    $tmp = [
                        "relation_type" => $noteEntityRelationItem['relation_type'],
                        "demand_type" => $data['note']['demand_type'],
                        "module_code" => $noteEntityRelationItem['relate_module_code'] ?? "",
                    ];
                    $demandTypeList[join("_", $tmp)] = $tmp;
                    if ($noteEntityRelationItem["relation_type"] === "step") {
                        $noteEntityRelationItem["step_id"] = $taskList[$noteEntityRelationItem["task_id"]]["step_id"];
                        $noteEntityRelationItem["step_category_id"] = $taskList[$noteEntityRelationItem["task_id"]]["step_category_id"];
                    }
                    // 增加环节需求查询当前实体下,是否有任务已经开始过了
                    if (!empty($noteEntityRelationItem['entity_id']) && !empty($noteEntityRelationItem['project_id'])) {
                        $taskIsStart = false;
                        if (in_array($data['note']['module_code'], ['asset', 'level'])) { // 资产,关卡
                            $assetDesignEntityIds = $this->getAssetDesignInfo($noteEntityRelationItem['entity_id'], $noteEntityRelationItem['project_id']);
                            if (!empty($assetDesignEntityIds)) {
                                $taskIsStart = TaskAbolitionService::getInstance()->getTaskStatus($noteEntityRelationItem['project_id'], $assetDesignEntityIds, $tmpNull);
                            }
                        }
                        if (in_array($data['note']['module_code'], ['shot', 'scene'])) { // 镜头,场
                            $shotEntityIds = $this->getShotStoryboardInfo($noteEntityRelationItem['entity_id'], $noteEntityRelationItem['project_id']);
                            if (!empty($shotEntityIds)) {
                                $taskIsStart = TaskAbolitionService::getInstance()->getTaskStatus($noteEntityRelationItem['project_id'], $shotEntityIds, $tmpNull);
                            }
                        }
                        $noteEntityRelationItem['task_is_start'] = NoteEntityRelationModel::TASK_IS_START_DEFAULT;
                        if ($taskIsStart) {
                            $noteEntityRelationItem['task_is_start'] = NoteEntityRelationModel::TASK_IS_START_YES;
                        }
                    }
                    // 增加工序任务需求,查询当前任务是否已经开始了
                    if ($noteEntityRelationItem['relation_type'] == NoteEntityRelationModel::RELATION_TYPE_STEP && !empty($noteEntityRelationItem['entity_id']) && !empty($noteEntityRelationItem['project_id']) && !empty($noteEntityRelationItem['task_id'])) {
                        $noteEntityRelationItem['task_is_start'] = NoteEntityRelationModel::TASK_IS_START_DEFAULT;
                        $nowTaskStatus = $taskList[$noteEntityRelationItem['task_id']]['task_status'];
                        if (!in_array($nowTaskStatus, $taskNotStartStartedStatusList)) {
                            $noteEntityRelationItem['task_is_start'] = NoteEntityRelationModel::TASK_IS_START_YES;
                        }
                    }
                    return $noteEntityRelationItem;
                }, $noteEntityRelationList);
            }

            if (!empty($noteCurrentModuleCode)) {
                $data['note']["module_code"] = $noteCurrentModuleCode;
            }
        }

        $noteEntityRelationData = false;
        $currentModuleLinkRelation = $noteModel->parserFilterModuleRelation(true);

        $noteModel->startTrans();//强制开启事务
        try {
            $relationIds = [];
            foreach ($data as $moduleCode => $item) {
                if (isset($currentModuleLinkRelation[$moduleCode]) && isset($mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]) && count($item) > 0) {
                    $mediaIds = [];
                    $mediaDataList = [];
                    $cosMediaDataList = [];
                    if (is_many_dimension_array($item)) {
                        foreach ($item as $mediaItem) {
                            if (empty($mediaItem)) {
                                continue;
                            }

                            if ($currentModuleLinkRelation[$moduleCode]['module_code'] === "media") {
                                $mediaService->generateMediaData($mediaItem);
                            }

                            $mediaData = $mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->addItem($mediaItem);

                            if (!$mediaData) {
                                throw new LogicException($mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getError(), ErrorCode::MEDIA_ADD_FAILURE);
                            } else {
                                $mediaIds[] = $mediaData['id'];
                                if ($currentModuleLinkRelation[$moduleCode]['module_code'] === 'media') {
                                    $mediaDataList[] = $mediaData;
                                } else {
                                    $cosMediaDataList[] = $mediaData;
                                }

                            }
                        }
                    }
                    $relationIds[$moduleCode] = join(',', $mediaIds);
                }
            }

            // 把水平自定义关联数据回插入主表
            foreach ($relationIds as $field => $relationId) {
                $data['note'][$field] = $relationId;
            }

            $commonService = CommonService::instance("note");
            $resData = $commonService->commonCreate(['data' => $data['note']], $noteModule);
            if (!empty($noteEntityRelationList)) {
                $noteEntityRelationList = $this->autoCompleteRelationNoteEntityRelationParam($noteEntityRelationList);
                $noteEntityRelationList = array_map(function ($noteEntityRelationItem) use ($resData) {
                    $noteEntityRelationItem["note_id"] = $resData["id"];
                    $noteEntityRelationItem["review_feedback_id"] = $resData["review_feedback_id"];
                    $noteEntityRelationItem["created"] = time();
                    $noteEntityRelationItem["created_by"] = $resData["created_by"];
                    return $noteEntityRelationItem;
                }, $noteEntityRelationList);
                $noteEntityRelationData = model(NoteEntityRelationModel::class)->addAll($noteEntityRelationList);


                //受影响的镜头
                if (!empty($data["affected_entity_relation"])) {
                    $affectedEntityRelationList = array_unique($data["affected_entity_relation"]);

                    // 将镜头替换为分镜
                    $shotList = model(EntityModel::class)
                        ->where(['id' => ["IN", $affectedEntityRelationList]])
                        ->field("id,module_id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.shot_storyboard')) as storyboard_id")
                        ->select();

                    $shotMap = [];
                    foreach ($shotList as $shotItem) {
                        if (module_by_id($shotItem["module_id"])['code'] == "shot") {
                            $shotMap[$shotItem["id"]] = $shotItem["storyboard_id"];
                        } else {
                            $shotMap[$shotItem["id"]] = $shotItem["id"];
                        }
                    }
                    //获取关联id
                    $noteEntityRelationListWhere = $noteEntityRelationList;
                    $noteEntityRelationListWhere["_logic"] = "OR";
                    $noteEntityRelationIds = model(NoteEntityRelationModel::class)->where($noteEntityRelationListWhere)->field("id")->select();
                    //组装数据
                    $affectedEntityRelationData = [];
                    foreach ($noteEntityRelationIds as $noteEntityRelationId) {
                        foreach ($affectedEntityRelationList as $affectedEntityRelationItem) {
                            $affectedEntityRelationData[] = [
                                "note_entity_relation_id" => $noteEntityRelationId["id"],
                                "entity_id" => $shotMap[$affectedEntityRelationItem] ?? $affectedEntityRelationItem,
                                "created" => time()
                            ];
                        }
                    }
                    if (!empty($affectedEntityRelationData)) {
                        model(RelationNoteEntityRelationModel::class)->addAll($affectedEntityRelationData);
                    }
                }
                $noteRelationAdded = [
                    'type' => 'add',
                    'changed' => $noteEntityRelationList,
                    'operate_at' => time()
                ];
                Hook::listen('note_relation_changed', $noteRelationAdded);

                // 如果是批量导入，则执行标签绑定
                if ($isImport) {
                    TagRelationService::getInstance()->noteBindTag($resData, $noteEntityRelationList);
                }
            }

            $noteModel->commit();

            // 消息推送 (找到相应的群) , 审核反馈的返修意见不推送
            if (!empty($noteEntityRelationData) && empty($resData["review_feedback_id"])) {
                $data = [
                    "type" => 'create_entity_demand_message',
                    "data" => [
                        "entity_ids" => $entityIds,
                        "demand_id" => $resData["id"],
                        "demand_type_list" => array_values($demandTypeList)
                    ]
                ];
                Client::send('send_entity_demand_relation_message', build_queue_data_with_xu($data));
            }
        } catch (Throwable $e) {
            $noteModel->rollback();
            throw $e;
        }

//        try {
//            $this->notifyTaskRelationPeople($resData);
//        } catch (\Throwable $e) {
//            trace(format_exception_info($e), 'ERR');
//        }

        if ($resultWanted) {
            $user = model(UserModel::class)->field("id,name,avatar")->find($resData["created_by"]);
            $lastUpdatedByUse = model(UserModel::class)->field("id,name,avatar")->find($resData['last_updated_by']);
            $resData["created_by"] = $user;
            $resData["last_updated_by_user"] = $lastUpdatedByUse;
            $resData["media"] = $mediaDataList ?? [];
            $resData["cos_media"] = $cosMediaDataList ?? [];
            if (!empty($noteEntityRelationList)) {
                $resData['note_entity_relation_list'] = $noteEntityRelationList;
            }
        }

        return $resData;

    }

    /**
     * 批量创建反馈
     * @param $data
     * @param $reviewRequestNodeId
     * @return bool|int|string
     * @throws Exception
     * @throws Throwable
     */
    public function batchCreateNote($data, $reviewRequestNodeId)
    {
        $mediaService = MediaService::getInstance();
        $noteModel = model(NoteModel::class);
        $noteModel->setCurrentModuleCode('note');
        $mediaModel = model(MediaModel::class);
        $cosMediaModel = model(CosMediaModel::class);
        $mediaMap = ["media" => $mediaModel, "cos_media" => $cosMediaModel];

        $count = 0;
        $noteMediaMap = [];
        $noteEntityRelationList = [];
        $projectId = 0;
        $episodeCodes = [];
        $entityIds = [];
        $demandTypeList = [];
        foreach ($data as $index => $dataItem) {
            if (!empty($dataItem["note_entity_relation"])) {
                // 去重
                foreach ($dataItem["note_entity_relation"] as $noteEntityRelationItem) {
                    $noteEntityRelationList[$index][join("_", $noteEntityRelationItem)] = $noteEntityRelationItem;
                }
            }
        }

        $noteEntityMap = [];
        $noteEpisodeCodeMap = [];
        foreach ($noteEntityRelationList as $index => $noteEntityRelationListItemData) {
            $noteEntityRelationListItemData = array_values($noteEntityRelationListItemData);
            $noteEntityRelationList[$index] = $noteEntityRelationListItemData;

            foreach ($noteEntityRelationListItemData as $noteEntityRelationListItem) {
                if (empty($projectId)) {
                    $projectId = $noteEntityRelationListItem["project_id"];
                }
                if ($noteEntityRelationListItem["relation_type"] === "episode") {
                    $episodeCodes[$noteEntityRelationListItem["episode_code"]] = $noteEntityRelationListItem["episode_code"];
                    $noteEpisodeCodeMap[$index][$noteEntityRelationListItem["episode_code"]] = $noteEntityRelationListItem["episode_code"];
                } else {
                    $entityIds[$noteEntityRelationListItem["entity_id"]] = $noteEntityRelationListItem["entity_id"];
                    $noteEntityMap[$index][$noteEntityRelationListItem["entity_id"]] = $noteEntityRelationListItem["entity_id"];
                }

                $tmp = [
                    "relation_type" => $noteEntityRelationListItem['relation_type'],
                    "demand_type" => $data[$index]['note']['demand_type'],
                    "module_code" => $noteEntityRelationListItem['relate_module_code'],
                ];
                $demandTypeList[$index][join("_", $tmp)] = $tmp;
            }
        }

        $noteEntityIdsMap = [];

        $entityFilter = [];
        if (!empty($entityIds)) {
            $entityIds = array_values($entityIds);
            $entityFilter = [
                "id" => ["IN", $entityIds]
            ];
        }


        if (!empty($episodeCodes)) {
            $episodeCodes = array_values($episodeCodes);
            if (!empty($entityFilter)) {
                $entityFilter = [
                    $entityFilter,
                    ["code" => ["IN", $episodeCodes], "project_id" => $projectId],
                    "_logic" => "OR"
                ];
            } else {
                $entityFilter = ["code" => ["IN", $episodeCodes], "project_id" => $projectId];
            }
        }

        $entityList = model(EntityModel::class)
            ->join("module on module.id = entity.module_id")
            ->where($entityFilter)
            ->field(["entity.id", "entity.module_id", "entity.code", "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_design'))) as design",
                "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.shot_storyboard')) as shot_storyboard",])
            ->select();

        $entityDataIds = [];

        $entityUpEntityIdMap = [];
        $episodeCodeMap = [];
        foreach ($entityList as $entityItem) {
            if ($entityItem["module_id"] === "episode") {
                $episodeCodeMap[$entityItem["code"]] = $entityItem["id"];
            }

            if (in_array($entityItem["module_id"], [module_by_code("asset")["id"], module_by_code("level")["id"]])) {
                $entityUpEntityIdMap[$entityItem["id"]] = intval($entityItem["design"]);
                $entityDataIds[] = intval($entityItem["design"]);
            } elseif ($entityItem["module_id"] === module_by_code("shot")["id"]) {
                $entityUpEntityIdMap[$entityItem["id"]] = intval($entityItem["shot_storyboard"]);
                $entityDataIds[] = intval($entityItem["shot_storyboard"]);
            } else {
                $entityDataIds[] = $entityItem["id"];
            }
        }

        foreach ($noteEpisodeCodeMap as $index => $noteEpisodeCodeItemData) {
            $noteEpisodeCodeMap[$index] = array_values(array_intersect_key($episodeCodeMap, $noteEpisodeCodeItemData));
        }

        // 替换为上级id
        $noteEntityMap = array_map(function ($noteEntityMapItem) {
            foreach ($noteEntityMapItem as $noteEntityMapDataIndex => $noteEntityMapDataItem) {
                if (isset($entityUpEntityIdMap[$noteEntityMapDataItem])) {
                    $noteEntityMapItem[$noteEntityMapDataIndex] = $entityUpEntityIdMap[$noteEntityMapDataItem];
                }
            }
            return $noteEntityMapItem;
        }, $noteEntityMap);

        foreach ($data as $index => $dataItem) {
            if (isset($noteEntityMap[$index])) {
                if (!isset($noteEntityIdsMap[$index])) {
                    $noteEntityIdsMap[$index] = array_values($noteEntityMap[$index]);
                } else {
                    $noteEntityIdsMap[$index] = array_merge($noteEntityIdsMap[$index], array_values($noteEntityMap[$index]));
                }
                $noteEntityIdsMap[$index] = array_unique($noteEntityIdsMap[$index]);
            }

            if (isset($noteEpisodeCodeMap[$index])) {
                if (!isset($noteEntityIdsMap[$index])) {
                    $noteEntityIdsMap[$index] = $noteEpisodeCodeMap[$index];
                } else {
                    $noteEntityIdsMap[$index] = array_merge($noteEntityIdsMap[$index], $noteEpisodeCodeMap[$index]);
                }
                $noteEntityIdsMap[$index] = array_unique($noteEntityIdsMap[$index]);
            }
        }

        //更换 关联的插入数据中上级id


        $noteModel->startTrans();
        try {
            foreach ($data as $index => $dataItem) {
                foreach ($dataItem as $moduleCode => $item) {
                    if (isset($currentModuleLinkRelation[$moduleCode]) && isset($mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]) && count($item) > 0) {
                        if (is_many_dimension_array($item)) {
                            foreach ($item as $mediaItem) {
                                if (empty($mediaItem)) {
                                    continue;
                                }

                                if ($currentModuleLinkRelation[$moduleCode]['module_code'] === "media") {
                                    $mediaService->generateMediaData($mediaItem);
                                }

                                $mediaData = $mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->addItem($mediaItem);

                                if (!$mediaData) {
                                    throw new LogicException($mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getError(), ErrorCode::MEDIA_ADD_FAILURE);
                                } else {
                                    $noteMediaMap[$index][$moduleCode][] = $mediaData['id'];
                                }
                            }
                        }
                    }
                }
            }

            $noteMediaList = array_map(function ($noteMediaMapItem) {
                foreach ($noteMediaMapItem as $noteMediaMapItemIndex => $noteMediaMapItemData) {
                    $noteMediaMapItem[$noteMediaMapItemIndex] = join(",", $noteMediaMapItemData);
                }
                return $noteMediaMapItem;
            }, $noteMediaMap);

            $noteMap = [];
            $noteUuids = [];

            foreach ($data as $index => $item) {
                $note = $item['note'];
                $note['review_request_node_id'] = $reviewRequestNodeId;
                $noteJson = ['role_code' => $note['role_code']];
                unset($note['role_code']);
                $note['created_by'] = fill_created_by();
                $note['last_updated_by'] = fill_created_by();
                $note['uuid'] = create_uuid();
                $noteUuids[$index] = $note['uuid'];
                $note['created'] = time();
                $note['last_updated'] = time();
                $note['tenant_id'] = auto_fill_tenant_id();
                if (isset($noteMediaList[$index])) {
                    $noteJson = array_merge($noteJson, $noteMediaList[$index]);
                }
                $note['json'] = json_encode($noteJson);
                $noteMap[] = $note;
            }

            $noteEntityIdsMapData = [];
            if (!empty($noteMap)) {
                $count = model(NoteModel::class)->addAll($noteMap);
                $noteList = model(NoteModel::class)->where(["uuid" => ["IN", array_values($noteUuids)]])->field("id,uuid")->select();
                $noteList = array_column($noteList, null, "uuid");

                $noteEntityRelationListData = [];
                foreach ($noteEntityRelationList as $index => $noteEntityRelationListItemData) {
                    if (isset($noteUuids[$index]) && isset($noteList[$noteUuids[$index]])) {
                        foreach ($noteEntityRelationListItemData as $noteEntityRelationListItem) {
                            $noteEntityRelationListItem["note_id"] = $noteList[$noteUuids[$index]]["id"];
                            $noteEntityRelationListItem["created"] = time();

                            //替换上级id
                            if (isset($entityUpEntityIdMap[$noteEntityRelationListItem["entity_id"]])) {
                                $noteEntityRelationListItem["entity_id"] = $entityUpEntityIdMap[$noteEntityRelationListItem["entity_id"]];
                            }

                            $noteEntityRelationListData[] = $noteEntityRelationListItem;
                        }
                    }
                }

                foreach ($noteEntityIdsMap as $index => $noteEntityIdsMapItem) {
                    if (isset($noteUuids[$index]) && isset($noteList[$noteUuids[$index]]["id"])) {
                        $noteEntityIdsMapData[$noteList[$noteUuids[$index]]["id"]] =
                            [
                                "entity_ids" => $noteEntityIdsMapItem,
                                "demand_id" => $noteList[$noteUuids[$index]]["id"],
                                "demand_type_list" => array_values($demandTypeList[$index])
                            ];
                    }
                }

                if (!empty($noteEntityRelationListData)) {
                    $noteRelationAdded = [
                        'type' => 'add',
                        'changed' => $noteEntityRelationListData,
                        'operate_at' => time()
                    ];
                    Hook::listen('note_relation_changed', $noteRelationAdded);

                    model(NoteEntityRelationModel::class)->addAll($noteEntityRelationListData);
                }
            }
            $noteModel->commit();

            // 消息推送 (找到相应的群)
            if (!empty($noteEntityIdsMapData)) {
                $data = [
                    "entity_ids" => $entityDataIds,
                    "note_entity_ids_map_data" => $noteEntityIdsMapData,
                ];
                Client::send('send_batch_create_entity_demand_message', build_queue_data_with_xu($data));
            }
        } catch (Throwable $e) {
            $noteModel->rollback();
            throw $e;
        }


        return $count;
    }

    /**
     * 通知任务关联的人员
     * @param array $resData
     */
    private function notifyTaskRelationPeople(array $resData)
    {
        $module = Module::$moduleDictData['module_index_by_id'][$resData['module_id']];

        switch ($module['code']) {
            case "task":
                $task = model(TaskModel::class)->find($resData['link_id']);
                $notifyUsers = [];
                $notifyUsers[] = $task['executor'];
                $notifyUsers[] = $task['assignee'];
                $notifyUsers[] = $resData['created_by'];


                $taskService = TaskService::getInstance();
                $relatedEpisode = $taskService->getTaskEpisode($task['id']);
                $episodeService = EpisodeAllocationService::getInstance();
                $episodeAllocation = $episodeService->getEpisodeAllocation(['entity_id' => $relatedEpisode['id'], 'project_id' => $task['project_id']]);
                if (!empty($episodeAllocation)) {
                    $notifyUsers[] = $episodeAllocation['user_id'];
                }
                $parentTask = $taskService->getParentTask($task['id'], $task['project_id']);

                /**
                 *
                 * 如果是影子任务
                 *
                 * 那么查询乙方任务相关人员
                 *
                 */
                if ($parentTask['type'] == 'shadow') {
                    $orderService = OrderService::getInstance();
                    $order = $orderService->getOrderByStepTaskId($task['id']);
                    $sourceTask = model(TaskModel::class)->find($order['current_task']['task_id']);

                    if (!empty($sourceTask)) {
                        $notifyUsers[] = $sourceTask['executor'];
                        $notifyUsers[] = $sourceTask['assignee'];
                        $relatedEpisode = $taskService->getTaskEpisode($sourceTask['id']);
                        $episodeAllocation = $episodeService->getEpisodeAllocation(['entity_id' => $relatedEpisode['id'], 'project_id' => $sourceTask['project_id']]);
                        if (!empty($episodeAllocation)) {
                            $notifyUsers[] = $episodeAllocation['user_id'];
                        }
                    }
                }
                $notifyUsers = array_unique($notifyUsers);
                $notifyUsers = model(UserModel::class)->where(['id' => ['IN', implode(',', $notifyUsers)]])->field("id,union_id,name")->select();
                $notifyUsers = array_column($notifyUsers, null, 'id');
                $reviewContent = str_repeat("[图片]", count(array_filter(explode(',', $resData['note_media'] ?? ''), function ($item) {
                        return !empty($item);
                    }))) . $resData['text'];
                $messageParam = [
                    'first' => '有新的反馈',
                    'task_name' => $task['name'],
                    'sender_name' => $notifyUsers[$resData['created_by']]['name'],
                    'send_at' => date("Y/m/d H:i", $resData['created']),
                    'send_content' => $reviewContent,
                    'remark' => '请及时处理'
                ];
                $messageTemplateId = $this->getTaskNoteNotifyTemplateId();

                foreach ($notifyUsers as $notifyUser) {
                    if ($notifyUser['id'] == $resData['created_by']) {
                        continue;
                    }

                    Client::send('send_notify_message_v2', build_queue_data_with_xu([
                        "param" => $messageParam,
                        'receiver' => $notifyUser['union_id'],
                        'template_id' => $messageTemplateId,
                        'content_type' => 'card_message',
                        'notice_type' => 'examine',
                    ]));
                }
                break;
        }
    }

    /**
     * 获得任务反馈通知模板id
     * @return int
     */
    private function getTaskNoteNotifyTemplateId()
    {
        $messageTemplateId = OptionsService::getInstance()->getOptionsConfigItemData("message_notify_template_config", "task_note_notify_template_id");
        return (int)$messageTemplateId;
    }

    /**
     * 查询反馈列表
     * @param array $filter
     * @param array $page
     * @param array|null $fields
     * @return array
     */
    public function selectNote(array $filter, array $page, array $fields = null)
    {
        $res = [
            'total' => 0,
            'rows' => [],
        ];


        $noteQueryFields = [
            'note.id', 'note.type', 'note.tenant_id', 'note.link_id', 'note.module_id', 'note.stick', 'note.parent_id',
            'note.text', 'note.last_updated', 'note.created_by', 'note.created', 'note.json', 'note.uuid',
            'note.version_id', 'note.review_param', 'note.version_check_list_id', 'note.is_feedback_show',
            'note.review_request_node_id', 'note.check_list_id', 'note.comment_subject_id', 'note.comment_floor_note_id',
            'module,id', 'module.code',
        ];

        $noteQueryFields = $fields ?? $noteQueryFields;

        $filter = parse_filter_to_tp_where($filter, 'note');
        $res['total'] = model(NoteModel::class)
            ->join('module on note.module_id = module.id', 'LEFT')
            ->where($filter)
            ->count();
        if ($res['total'] === 0) {
            return $res;
        }
        $res['rows'] = model(NoteModel::class)
            ->join('module on note.module_id = module.id', 'LEFT')
            ->where($filter)
            ->field(build_complex_query_fields($noteQueryFields, '__'))
            ->page(...$page)
            ->select();
        if (empty($res['rows'])) {
            return $res;
        }

        $res['rows'] = $this->dealNoteMediaAndUser($res['rows'], ['id', 'parent_id', 'comment_floor_note_id']);

        if (array_key_exists('note.comment_floor_note_id', $filter) && $filter['note.comment_floor_note_id'] == 0) {
            // 说明是楼层查询
            $this->appendFloorReplyNoteList($res, $noteQueryFields);
        }

        $this->appendNoteParent($res['rows'], $noteQueryFields);
        return $res;
    }

    /**
     * 查询反馈 包含用户详情 部门 角色信息
     * @param $param
     * @param $tenantId
     * @return array
     */
    public function selectWithUserDetail($param, $tenantId)
    {
        $page = $param['page'] ?? [1, C('default_page_total')];
        $data = $this->selectNote($param['filter'], $page);

        if (count($data['rows']) > 0) {
            $noteList = array_column($data['rows'], 'note');
            $roleCodeList = array_column($noteList, 'role_code', 'role_code');
            unset($roleCodeList['']);
            $roleList = [];
            if (!empty($roleCodeList)) {
                $requestData = ['param' => ['filter' => [
                    'role.code' => ['-in', implode(',', $roleCodeList)]
                ]]];
                $roleList = teamones_request('teamones-im', 'role/select', $requestData)['data'];
                $roleList = array_column($roleList['rows'], 'name', 'code');
            }

            $versionIdList = array_column($noteList, 'version_id', 'version_id');
            unset($versionIdList[0]);
            $versionList = [];
            if (count($versionIdList) > 0) {
                $versionList = model(VersionModel::class)->where(['id' => ['IN', array_values($versionIdList)]])->field('id,number')->select();
                $versionList = array_column($versionList, null, 'id');
            }

            $userUnionIdList = [];
            $tenantIds = [];
            foreach ($data['rows'] as $rowItem) {
                if (!empty($rowItem['user'])) {
                    $userUnionIdList[$rowItem['user']['union_id']] = $rowItem['user']['union_id'];
                }
                //排除当前租户
                if (!empty($rowItem['note']['tenant_id']) && $tenantId != $rowItem['note']['tenant_id']) {
                    $tenantIds[$rowItem['note']['tenant_id']] = $rowItem['note']['tenant_id'];
                }
                // 处理楼层note查询
                if (!empty($rowItem['note']['comment_floor_note_id'])) {

                }
            }

            $tenantInfo = TaskService::getInstance()->queryTaskSupplierInfo($tenantId, [], [], [], [], $tenantIds);
            $tenantInfo = $tenantInfo['tenant'];

            if (count($userUnionIdList) > 0) {
                $paramRemote = [
                    "param" => [
                        'filter' => [
                            'user_id' => ['-in', $userUnionIdList],
                            'tenant_id' => $tenantId,
                        ],
                    ]
                ];
                $tenantService = TenantService::getInstance();
                $userList = $tenantService->selectDepartmentUser($tenantId, $paramRemote);
                $userList = array_column($userList['rows'], null, 'saas_user_id');
                foreach ($data['rows'] as &$row) {
                    $row['user_detail'] = null;

                    //更改姓名
                    if (!empty($row['user']) && isset($tenantInfo[$row['note']['tenant_id']])) {
                        $row['user']['name'] = $tenantInfo[$row['note']['tenant_id']]['name'];
                    }

                    if (!empty($row['user']) && !empty($userList[$row['user']['id']])) {
                        $row['user_detail'] = $userList[$row['user']['id']] ?? null;
                        // 如果存在role_code 那么就尝试在用户的部门信息里遍历到role_code的信息
                    }
                    $row['version'] = null;
                    if (isset($row['note']['version_id']) && $row['note']['version_id'] > 0) {
                        $row['version'] = $versionList[$row['note']['version_id']] ?? null;
                    }
                }
            }
        }
        return $data;
    }

    /**
     * 获取版本反馈
     * @param $versionId
     * @param $tenantId
     * @param array $page
     * @return array
     * @throws \Exception
     */
    public function getVersionNote($versionId, $tenantId, array $page = [])
    {
//        $noteFilter = [
//            'filter' => [
//                'note.version_id' => ['IN', $versionId],
//                'note.module_id' => Module::$moduleDictData['module_index_by_code']['task']['id'],
//                'note.is_feedback_show' => 'yes'
//            ],
//            'fields' => 'note.version_id,note.created,note.version_check_list_id,note.review_param,note.id,note.tenant_id,note.last_updated,note.text,note.review_request_node_id,note.link_id,note.role_code,' .
//                'module.id,module.name,module.code,module.type,note_media.thumb,note_media.md5_name,note_media.param,note_media.size,note_media.type,user.id,user.name,user.avatar,user.email,user.union_id',
//        ];
//        $noteFilter['page'] = $page;
//        $commonService = CommonService::instance('note');
//        $noteData = $commonService->select($noteFilter, 'note');
        $fields = ['note.version_id', 'note.created', 'note.version_check_list_id', 'note.review_param', 'note.id', 'note.created_by',
            'note.tenant_id', 'note.last_updated', 'note.text', 'note.review_request_node_id', 'note.link_id', 'note.role_code', 'note.json'];
        //判定甲乙方 (乙方需要看到甲方被 乙方商务制片接收修改后的审核反馈) , (甲方只能看到自己的)
        $version = model(VersionModel::class)->find($versionId);

        if (empty($version)) {
            throw new LogicException("version not found", ErrorCode::VERSION_NOT_FOUND);
        }
        $task = model(TaskModel::class)->find($version['link_id']);
        if (empty($task)) {
            throw new LogicException("task not found", ErrorCode::TASK_NOT_FOUND);
        }
        $noteData = [];
        list($filter, , $realVersion) = $this->buildBaseVersionFilter($tenantId, $task, $version);
        $reviewRequestFilter = [
            'review_request.tenant_id' => $task['tenant_id'],
            'review_request.link_id' => $realVersion['id'],
            'review_request.module_id' => module_by_code("version")["id"],
            'review_request.is_reviewed' => "yes",
            'review_request.is_pass' => "no",
            'review_request_node.is_reviewed' => "yes",
            'review_request_node.is_pass' => "no",
            'review_request_node.is_second_confirm' => "yes",
        ];

        $reviewRequestNode = model(ReviewRequestModel::class)
            ->join("review_request_node on review_request_node.review_request_id = review_request.id")
            ->where($reviewRequestFilter)
            ->field("review_request_node.role_code")
            ->find();

        $noteList = model(NoteModel::class)->join("note_entity_relation on note_entity_relation.note_id = note.id")
            ->where($filter)->field(build_complex_query_fields($fields, '__'))->page(...$page)->select();

        $noteData["rows"] = $this->dealNoteMediaAndUser($noteList);

        if (empty($noteData['rows'])) {
            return [];
        }

        //添加用户role_code 添加 version_checklist content
        $versionCheckListIds = [];
        $roleCodes = [];
        if (!empty($reviewRequestNode["role_code"])) {
            $roleCodes[] = $reviewRequestNode["role_code"];
        }

        foreach ($noteData['rows'] as $noteListItem) {
            if (!array_key_exists($noteListItem['note']['version_check_list_id'], $versionCheckListIds)) {
                $versionCheckListIds[] = $noteListItem['note']['version_check_list_id'];
            }

            if (!array_key_exists($noteListItem['note']['role_code'], $versionCheckListIds)) {
                $roleCodes[] = $noteListItem['note']['role_code'];
            }
        }

        $contentMap = model(VersionCheckListModel::class)->where(['id' => ["IN", $versionCheckListIds]])->field('id,content')->select();
        $contentMap = array_column($contentMap, 'content', 'id');

        $roleNames = [];
        if (!empty($roleCodes)) {
            $roleCodes = array_unique($roleCodes);
            $reviewTaskService = ReviewTaskService::getInstance();
            $roleNames = $reviewTaskService->getRoleName($roleCodes);
            if (!empty($roleNames)) {
                $roleNames = array_column($roleNames, 'name', 'code');
            }
        }

        foreach ($noteData['rows'] as &$noteItem) {
            $noteItem['note']['role_code_name'] = "";

            if (array_key_exists($noteItem['note']['role_code'], $roleNames)) {
                $noteItem['note']['role_code_name'] = $roleNames[$noteItem['note']['role_code']];
            } elseif (isset($reviewRequestNode["role_code"]) && isset($roleNames[$reviewRequestNode["role_code"]])) {
                $noteItem['note']['role_code_name'] = $roleNames[$reviewRequestNode["role_code"]];
            }


            if (array_key_exists($noteItem['note']['version_check_list_id'], $contentMap)) {
                $noteItem['note']['content'] = $contentMap[$noteItem['note']['version_check_list_id']];
            }
        }

        //TODO 修复版本审核完不通过不显示的数据
        return $noteData;
    }

    /**
     * 处理封面和用户
     * @param $notes
     * @param null $outsideKeys
     * @return mixed
     */
    public function dealNoteMediaAndUser($notes, $outsideKeys = null)
    {
        $userIdList = [];
        $noteMediaIdList = [];
        $noteCosMediaIdList = [];
        foreach ($notes as &$note) {
            $note = separate_complex_query_data($note, '__');
            CommonService::getFormatAttrData(model(NoteModel::class), $note['note']);
            $note['note'] = CommonService::appendJsonFields($note['note'], 'json');
            if (isset($note['note']['note_media'])) {
                $note['note']['note_media'] = explode(',', $note['note']['note_media']);
                foreach ($note['note']['note_media'] as $mediaId) {
                    $noteMediaIdList[$mediaId] = $mediaId;
                }
            }

            if (isset($note['note']['note_cos_media'])) {
                $note['note']['note_cos_media'] = explode(',', $note['note']['note_cos_media']);
                foreach ($note['note']['note_cos_media'] as $cosMediaId) {
                    $noteCosMediaIdList[$cosMediaId] = $cosMediaId;
                }
            }

            $userIdList[$note['note']['created_by']] = $note['note']['created_by'];
            if (!empty($outsideKeys)) {
                foreach ($outsideKeys as $k) {
                    $note['note_' . $k] = $note['note'][$k] ?? null;
                }
            }
        }
        $mediaQueryFields = [
            'id',
            'thumb',
            'param',
            'size',
            'type'
        ];
        $mediaList = [];
        if (!empty($noteMediaIdList)) {
            $mediaList = model(MediaModel::class)->where(['id' => ['IN', array_keys($noteMediaIdList)]])->field($mediaQueryFields)->select();
            $mediaList = array_map(function ($item) {
                return CommonService::getFormatAttrData(model(MediaModel::class), $item);
            }, $mediaList);
            $mediaList = array_column($mediaList, null, 'id');
        }
        $cosMediaList = [];
        if (!empty($noteCosMediaIdList)) {
            $cosMediaList = model(CosMediaModel::class)->where(['id' => ['IN', array_keys($noteCosMediaIdList)]])->select();
            $cosMediaList = array_column($cosMediaList, null, 'id');
        }

        $userList = [];
        if (!empty($userIdList)) {
            $userQueryFields = [
                'id',
                'name',
                'union_id',
                'avatar',
            ];
            $userList = model(UserModel::class)->where(['id' => ['IN', array_keys($userIdList)]])->field($userQueryFields)->select();
            $userList = array_column($userList, null, 'id');
        }

        foreach ($notes as &$note) {
//            $note = separate_complex_query_data($note, '__');
//            CommonService::getFormatAttrData(model(NoteModel::class), $note['note']);
//            $note['note'] = CommonService::appendJsonFields($note['note'], 'json');
            if (isset($note['note']['note_media'])) {
                $noteMedia = [];
                foreach ($note['note']['note_media'] as $mediaId) {
                    if (isset($mediaList[$mediaId])) {
                        $noteMedia[] = $mediaList[$mediaId];
                    }
                }
                $note['note_media'] = $noteMedia;
            }
            if (isset($note['note']['note_cos_media'])) {
                $cosNoteMedia = [];
                foreach ($note['note']['note_cos_media'] as $cosMediaId) {
                    if (isset($cosMediaList[$cosMediaId])) {
                        $cosNoteMedia[] = $cosMediaList[$cosMediaId];
                    }
                }
                $note['note_cos_media'] = $cosNoteMedia;
            }
            $note['user'] = $userList[$note['note']['created_by']] ?? null;
        }
        return $notes;
    }

    /**
     * 获取需求列表
     * @param $param
     * @return array|false|mixed|string
     */
    public function getDemandNoteList($param)
    {
        //是否显示分页--默认显示
        $showPage = $param['show_page'] ?? 1;
        $page = $param['page'] ?? [1, C('default_page_total')];
        //不显示分页 --- 任务库所有有可能超过1000
        if (!$showPage) {
            $page = [1, C("database.database_max_select_rows") * 10];
        }

        // 默认倒叙
        $order = $param['order'] ?? 'note.created desc';
        $fields = ['note.id', 'note.created_by', 'note.created', 'note.module_code', 'note.last_updated_by', 'note.repair_step_id', 'note.proposer',
            'note.proposer_department', 'note.last_updated', 'note.demand_type', 'note.text', 'note.json', 'note.review_request_node_id', 'note.task_repair_order_id',
            'note_entity_relation.task_is_start', 'note_entity_relation.task_id', 'note.demand_type'];

        // 获取需求类型
        $demandType = ['feedback', 'review_feedback', 'demand', 'repair_feedback', 'task_progress'];

        $entityProjectIds = $param["filter"]['entity_project_ids'] ?? [];
        unset($param["filter"]['entity_project_ids']);
        $filter = $param["filter"] ?? [];

        if (isset($filter["is_relation"])) {
            if ($filter["is_relation"]) {
                $filter["note_entity_relation.id"] = ["GT", 0];
            } else {
                $filter["_string"] = "note_entity_relation.id is null";
            }
            unset($filter["is_relation"]);
        }

        // 如果是场次需求,同一场,同一集下可以相互看
        if (!empty($filter['scene']) && !empty($filter['note_entity_relation.project_id']) && !empty($filter['episode_code'])) {
            $filter = [
                'scene' => $filter['scene'],
                'episode_code' => $filter['episode_code'],
                'note_entity_relation.project_id' => $filter['note_entity_relation.project_id'] ?? 0,
                'note.project_id' => $filter['note_entity_relation.project_id'] ?? 0,
            ];
        }


        //如果审核反馈的不存在,不能看到未发布的,只能看到发布的(进行中,和已完成的)
        if (!isset($filter['note.review_feedback_id'])) {
            $filter = [
                $filter,
                [
                    ['note.review_feedback_id' => 0],
                    [
                        'note.review_feedback_id' => ['GT', 0],
                        'note.demand_status' => ["IN", [NoteModel::DEMAND_STATUS_PROCESSING, NoteModel::DEMAND_STATUS_FINAL, NoteModel::DEMAND_STATUS_PENDING]]
                    ],
                    "_logic" => "OR"
                ]
            ];
        }

        $group = 'note.id';
        if (!empty($param['get_last_task_note']) && $param['get_last_task_note'] == 'yes') {
            $group = 'note_entity_relation.task_id';
        }

        if ($entityProjectIds) {
            $filter['note.project_id'] = ["IN", $entityProjectIds];
        }

        $filter['note.demand_type'] = ["IN", $demandType];
        $filter['note_entity_relation.repair_accept_status'] = ['in', ['default', 'yes']];

        $demandList = model(NoteEntityRelationModel::class)
            ->join("LEFT JOIN note on note_entity_relation.note_id = note.id")
            ->field($fields)
            ->where($filter)
            ->order($order)
            ->group($group)
            ->page(...$page)
            ->select();

        if (empty($demandList)) {
            return [];
        }

        $medias = [];
        $cosMedias = [];
        $userIds = [];
        $demandIds = [];
        $feedbackNodeMap = [];
        $taskRepairOrderIdList = [];
        $departmentIds = [];
        foreach ($demandList as $index => $demandItem) {
            $demandItem["json"] = json_decode($demandItem["json"] ?? '{}', true);
            if (!empty($demandItem["json"])) {
                $noteMedia = [];
                if (!empty($demandItem["json"]["note_media"])) {
                    $noteMedia = explode(",", $demandItem["json"]["note_media"]);
                }

                $medias = array_merge($medias, $noteMedia);
                $demandItem["media"] = $noteMedia;

                $noteCosMedia = [];
                if (!empty($demandItem["json"]["note_cos_media"])) {
                    $noteCosMedia = explode(",", $demandItem["json"]["note_cos_media"]);
                }

                $cosMedias = array_merge($cosMedias, $noteCosMedia);
                $demandItem["cos_media"] = $noteCosMedia;
            }

            if (!empty($demandItem["proposer"])) {
                $userIds[$demandItem["proposer"]] = $demandItem["proposer"];
            }

            if (!empty($demandItem["proposer_department"])) {
                $departmentIds[$demandItem["proposer_department"]] = $demandItem["proposer_department"];
            }

            $userIds[$demandItem["created_by"]] = $demandItem["created_by"];
            $userIds[$demandItem["last_updated_by"]] = $demandItem["last_updated_by"];
            if (!empty($demandItem["created_by"])) {
                $userIds[$demandItem["created_by"]] = $demandItem["created_by"];
            }
            if (!empty($demandItem["last_updated_by"])) {
                $userIds[$demandItem["last_updated_by"]] = $demandItem["last_updated_by"];
            }
            $demandIds[$demandItem["id"]] = $demandItem["id"];

            if (!empty($demandItem['task_repair_order_id']) && $demandItem['task_repair_order_id'] > 0) {
                $taskRepairOrderIdList[$demandItem['task_repair_order_id']] = $demandItem['task_repair_order_id'];
            }
            if (!empty($demandItem['demand_type']) && $demandItem['demand_type'] == NoteModel::DEMAND_TYPE_REVIEW_FEEDBACK && !empty($demandItem['review_request_node_id'])) {
                $feedbackNodeMap[$demandItem['id']] = $demandItem['review_request_node_id'];
            }

            unset($demandItem["json"]);
            $demandList[$index] = $demandItem;
        }

        //查询需求下的 关联关系
        $noteEntityRelationList = model(NoteEntityRelationModel::class)->where(['note_id' => ["IN", $demandIds]])->field("note_id,relation_type")->group("note_id,relation_type")->select();

        $noteRelationTypeMap = [];
        foreach ($noteEntityRelationList as $noteEntityRelationItem) {
            $noteRelationTypeMap[$noteEntityRelationItem["note_id"]][] = $noteEntityRelationItem["relation_type"];
        }

        $userList = [];
        if (!empty($userIds)) {
            $userList = model(UserModel::class)->where(["id" => ["IN", $userIds]])->field("id,name,union_id,avatar")->select();
            $userList = array_column($userList, null, "id");
        }

        $departmentMap = [];
        if (!empty($departmentIds)) {
            $departmentList = teamones_request('teamones-im', 'department/select', [
                'param' => [
                    'filter' => [
                        'id' => ['-in', join(",", $departmentIds)],
                    ],
                    'fields' => 'id,name'
                ]
            ]);
            if (!empty($departmentList['data']['rows'])) {
                $departmentMap = array_column($departmentList['data']['rows'], null, 'id');
            }
        }

        //图片
        if (!empty($medias)) {
            $mediaList = model(MediaModel::class)->where([
                'id' => ["IN", $medias]
            ])->field('id,thumb,param')->select();
            $mediaList = array_map(function ($mediaItem) {
                $mediaItem['param'] = json_decode($mediaItem['param'], true);
                return $mediaItem;
            }, $mediaList);

            $mediaList = array_column($mediaList, null, "id");
        }

        if (!empty($cosMedias)) {
            $cosMediaList = model(CosMediaModel::class)->where(['id' => ["IN", $cosMedias]])->select();
            $cosMediaList = array_column($cosMediaList, null, "id");
            $cosMediaList = array_map(function ($tmp) {
                $tmp['param'] = json_decode($tmp['param'], true);
                return $tmp;
            }, $cosMediaList);
        }

        // 增加评论人头像
        $commentList = $this->getCommentUserList($demandIds);

        // 查询审核反馈结果
        $feedbackNodeResult = [];
        if (!empty($feedbackNodeMap)) {
            $feedbackNodeResult = $this->getReviewFeedbackDemandResult($feedbackNodeMap);
        }
        // 查询返修单 返修任务信息
        $noteTaskRepairInfo = [];
        if (!empty($taskRepairOrderIdList)) {
            $noteTaskRepairInfoFields = [
                'task_repair_order.id',
                'task.id',
                'task.name',
                'step_category.id',
                'step_category.name',
                'entity.id',
                'entity.initial_episode_code',
                'entity.showings_number',
                'entity.name',
            ];

            $noteTaskRepairInfo = model(TaskRepairOrderModel::class)
                ->join('task on task.id = task_repair_order.repair_task_id')
                ->join('step_category on step_category.id = task.step_category_id')
                ->join('entity on entity.id = task.entity_id')
                ->where(['task_repair_order.id' => ['IN', implode(',', $taskRepairOrderIdList)]])
                ->field(build_complex_query_fields($noteTaskRepairInfoFields, '__'))
                ->select();
            foreach ($noteTaskRepairInfo as &$noteTaskRepairInfoItem) {
                $noteTaskRepairInfoItem = separate_complex_query_data($noteTaskRepairInfoItem, '__');
                $noteTaskRepairInfoItem['task_repair_order_id'] = $noteTaskRepairInfoItem['task_repair_order']['id'];
            }
            $noteTaskRepairInfo = array_column($noteTaskRepairInfo, null, 'task_repair_order_id');
        }

        // 填补
        foreach ($demandList as $index => $demandItem) {
            //图片
            $mediaTmp = [];
            $cosMediaTmp = [];
            if (!empty($demandItem["media"])) {
                foreach ($demandItem["media"] as $mediaId) {
                    if (isset($mediaList[$mediaId])) {
                        $mediaTmp[] = $mediaList[$mediaId];
                    }
                }
            }
            if (!empty($demandItem["cos_media"])) {
                foreach ($demandItem["cos_media"] as $cosMediaId) {
                    if (isset($cosMediaList[$cosMediaId])) {
                        $cosMediaTmp[] = $cosMediaList[$cosMediaId];
                    }
                }
            }

            $demandItem["media"] = $mediaTmp;
            $demandItem["cos_media"] = $cosMediaTmp;
            //创建人
            if (!empty($demandItem["created_by"]) && isset($userList[$demandItem["created_by"]])) {
                $demandItem["created_by_user"] = $userList[$demandItem["created_by"]];
            }

            // 最后编辑人
            if (!empty($demandItem["last_updated_by"]) && isset($userList[$demandItem["last_updated_by"]])) {
                $demandItem["last_updated_by_user"] = $userList[$demandItem["last_updated_by"]];
            }

            //提出人
            $demandItem["proposer"] = $userList[$demandItem["proposer"]] ?? null;
            $demandItem["proposer_department"] = $departmentMap[$demandItem["proposer_department"]] ?? null;

            //关联关系补充
            if (!empty($demandItem["id"]) && !empty($noteRelationTypeMap[$demandItem["id"]])) {
                $demandItem["relation_type"] = $noteRelationTypeMap[$demandItem["id"]];
            }

            // 评论人数以及评论头像补充
            if (isset($commentList[$demandItem['id']])) {
                $demandItem['comment_list'] = array_slice($commentList[$demandItem['id']], 0, 9);
                $demandItem['comment_count'] = count($commentList[$demandItem['id']]);
                $demandItem['comment_sum'] = 16; //待定 todo :不知道产品需要查询的评论数量是什么数量
            }

            // 审核反馈结果
            if (isset($feedbackNodeResult[$demandItem['id']])) {
                $demandItem['review_feedback_result'] = $feedbackNodeResult[$demandItem['id']]['is_pass'];
            }
            // 返修任务信息
            if (!empty($demandItem['task_repair_order_id']) && isset($noteTaskRepairInfo[$demandItem['task_repair_order_id']])) {
                $demandItem['start_repair_info'] = $noteTaskRepairInfo[$demandItem['task_repair_order_id']];
            }

            $demandList[$index] = $demandItem;
        }

        return $demandList;
    }


    /**
     * 通过指定任务ids获取最新一条note数据
     * @param $taskIdsFilter
     * @param $fields
     * @return array|false|mixed|string
     */
    public function getLastDemandNoteListTextByTaskIds($taskIdsFilter, $fields = '')
    {
        if (empty($taskIdsFilter['task_id'])) {
            return [];
        }
        // 获取需求类型
        $filter = [
            'note_entity_relation.task_id' => $taskIdsFilter['task_id'],
            'note_entity_relation.repair_accept_status' => ['in', ['default', 'yes']],
            'note.demand_type' => ['in', ['feedback', 'review_feedback', 'demand', 'repair_feedback']]
        ];

        // 按最新分组
        $group = 'note_entity_relation.task_id';

        //不显示分页 --- 任务库所有有可能超过1000
        $page = [1, C("database.database_max_select_rows") * 10];

        $demandList = model(NoteEntityRelationModel::class)
            ->join("LEFT JOIN note on note_entity_relation.note_id = note.id")
            ->field('max(note.id) as max_note_id,note_entity_relation.task_id')
            ->where($filter)
            ->order('note.created desc')
            ->group($group)
            ->page(...$page)
            ->select();
        if (empty($demandList)) {
            return [];
        }
        $maxNoteIds = [];
        $returnData = [];
        foreach ($demandList as $demandItem) {
            $maxNoteIds[] = $demandItem['max_note_id'];
        }
        $maxNoteList = model(NoteModel::class)->where(['id' => ['IN', $maxNoteIds]])->field('id,text')->select();
        $maxNoteMap = array_column($maxNoteList, null, 'id');
        foreach ($demandList as $demandItem) {
            $returnData[] = [
                'id' => $demandItem['max_note_id'],
                'task_id' => $demandItem['task_id'],
                'text' => $maxNoteMap[$demandItem['max_note_id']]['text'],
            ];
        }
        return $returnData;
    }

    /**
     * 获取需求的详细信息
     * @param $filter
     * @return array|false|mixed|string|null
     */
    public function demandInformation($filter)
    {
        $demand = model(NoteModel::class)->where($filter)->field("id,text,json,demand_type,module_code,project_id,created_by")->find();
        if (empty($demand)) {
            return [];
        }

        //获取关联类型
        $noteEntityRelation = model(NoteEntityRelationModel::class)->where(['note_id' => $demand["id"]])->field("relation_type,episode_code,entity_id,task_id")->select();
        $demand["relation_type"] = null;
        $entityIds = [];
        if (!empty($noteEntityRelation)) {
            $demand["relation_type"] = array_values(array_column($noteEntityRelation, "relation_type", "relation_type"));
            foreach ($noteEntityRelation as $noteEntityRelationItem) {
                if ($noteEntityRelationItem["relation_type"] === "step") {
                    $taskIds[$noteEntityRelationItem["task_id"]] = $noteEntityRelationItem["task_id"];
                    $entityIds[$noteEntityRelationItem["entity_id"]] = $noteEntityRelationItem["entity_id"];
                }
                if ($noteEntityRelationItem["relation_type"] === "entity") {
                    $entityIds[$noteEntityRelationItem["entity_id"]] = $noteEntityRelationItem["entity_id"];
                }
            }

            $task = [];
            if (!empty($taskIds)) {
                $taskIds = array_values($taskIds);
                $task = model(TaskModel::class)->where(["id" => ["IN", $taskIds]])->field("id,name")->select();
                $task = array_column($task, null, "id");
            }

            $entity = [];
            if (!empty($entityIds)) {
                $entityIds = array_values($entityIds);
                $entity = model(EntityModel::class)->where(["id" => ["IN", $entityIds]])->field("id,name,showings_number")->select();
                $entity = array_column($entity, null, "id");
            }

            $noteEntityRelation = array_map(function ($noteEntityRelationItem) use ($entity, $task) {
                $noteEntityRelationItem["entity"] = $entity[$noteEntityRelationItem["entity_id"]] ?? null;
                $noteEntityRelationItem["task"] = $task[$noteEntityRelationItem["task_id"]] ?? null;
                return $noteEntityRelationItem;
            }, $noteEntityRelation);

            $demand["note_entity_relation"] = $noteEntityRelation;
        }

        $user = model(UserModel::class)->field("id,name,avatar")->find($demand["created_by"]);
        $demand["created_by"] = $user;

        // 获取图片
        $json = json_decode($demand['json'], true);
        unset($demand['json']);
        $noteMedia = $json["note_media"] ?? [];
        if (!empty($noteMedia)) {
            $mediaList = model(MediaModel::class)->where([
                'id' => ["IN", $noteMedia]
            ])->field('id,thumb,param')->select();
            $mediaList = array_map(function ($mediaItem) {
                $mediaItem['param'] = json_decode($mediaItem['param'], true);
                return $mediaItem;
            }, $mediaList);
            $demand['media'] = $mediaList;
        }

        //获取附件
        $noteCosMedia = $json["note_cos_media"] ?? [];
        if (!empty($noteCosMedia)) {
            $cosMediaList = model(CosMediaModel::class)->where([
                'id' => ["IN", $noteCosMedia]
            ])->field('id,object,file_name,cos_key,param')->select();
            $cosMediaList = array_map(function ($tmp) {

                $tmp['param'] = json_decode($tmp['param'], true);
                return $tmp;
            }, $cosMediaList);
            $demand['note_cos_media'] = $cosMediaList;
        }

        return $demand;
    }

    /**
     * 获取需求实体列表
     * @param $filter
     * @return array
     * @throws \Exception
     */
    public function getDemandEntityList($filter)
    {
        $demandType = ['feedback', 'review_feedback', 'demand', 'repair_feedback'];
        $page = $param["page"] ?? [1, C("database.database_max_select_rows")];

        $demandList = model(NoteModel::class)
            ->join("note_entity_relation on note_entity_relation.note_id = note.id")
            ->where(["demand_type" => ["IN", $demandType]])
            ->where($filter)
            ->field("note.id as note_id,note_entity_relation.id as note_entity_relation_id,note_entity_relation.relation_type,note_entity_relation.project_id,note_entity_relation.relate_module_code,note_entity_relation.episode_code,note_entity_relation.entity_id,note_entity_relation.task_id")
            ->page(...$page)
            ->select();

        if (empty($demandList)) {
            return [];
        }

        $taskIds = [];
        $entityIds = [];
        $episodeCodes = [];
        $projectId = 0;
        foreach ($demandList as $demandItem) {
            if ($demandItem["relation_type"] === "step") {
                $taskIds[$demandItem["task_id"]] = $demandItem["task_id"];
                $entityIds[$demandItem["entity_id"]] = $demandItem["entity_id"];
            }
            if ($demandItem["relation_type"] === "entity") {
                $entityIds[$demandItem["entity_id"]] = $demandItem["entity_id"];
            }
            if ($demandItem["relation_type"] === "episode") {
                if (empty($projectId)) {
                    $projectId = $demandItem["project_id"];
                }
                $entityIds[$demandItem["entity_id"]] = $demandItem["entity_id"];
                $episodeCodes[$demandItem["episode_code"]] = $demandItem["episode_code"];
            }
        }

        $taskIds = array_values($taskIds);
        $entityIds = array_values($entityIds);
        $episodeCodes = array_values($episodeCodes);

        $task = [];
        if (!empty($taskIds)) {
            $task = model(TaskModel::class)->where(["id" => ["IN", $taskIds]])->field("id,name")->select();
            $task = array_column($task, null, "id");
        }

        $entity = [];
        if (!empty($entityIds)) {
            $entity = model(EntityModel::class)->where(["id" => ["IN", $entityIds]])->field("id,name,showings_number")->select();
            $entity = array_column($entity, null, "id");
        }


        $episodeCodeMap = [];
        if (!empty($episodeCodes)) {
            $episode = model(EntityModel::class)->where(["code" => ["IN", $episodeCodes], "project_id" => $projectId])->select();

            foreach ($episode as $episodeItem) {
                $episodeCodeMap[$episodeItem["code"]] = $episodeItem["id"];
                $entityIds[] = $episodeItem["id"];
            }
        }


        //批量查询会话
        $chat = [];
        if (!empty($entityIds)) {
            $chatService = ChatService::getInstance();
            $chat = $chatService->selectBatchEntityChat($entityIds);
        }


        return array_map(function ($demandItem) use ($chat, $entity, $task, $episodeCodeMap) {
            //填补会话
            $demandItem["chat"] = null;
            if ($demandItem["relation_type"] === "episode") {
                if (isset($episodeCodeMap[$demandItem["episode_code"]]) && isset($chat[$episodeCodeMap[$demandItem["episode_code"]]])) {
                    $demandItem["chat"] = $chat[$episodeCodeMap[$demandItem["episode_code"]]];
                }
            } else {
                $demandItem["chat"] = $chat[$demandItem["entity_id"]] ?? null;

                //填补实体名称
                $demandItem["entity"] = $entity[$demandItem["entity_id"]] ?? null;

                //填补任务名称
                $demandItem["task"] = $task[$demandItem["task_id"]] ?? null;
            }

            return $demandItem;
        }, $demandList);
    }


    /**
     * 删除实体关联关系
     * @param $id
     * @return false|float|int|mixed|string
     */
    public function delDemandRelationEntity($id)
    {
        $noteEntityRelationData = model(NoteEntityRelationModel::class)->where(['id' => $id])->field('id')->find();
        if (empty($noteEntityRelationData)) {
            throw new LogicException('note entity relation not find', ErrorCode::NOTE_ENTITY_RELATION_NOT_FIND);
        }
        model(NoteEntityRelationModel::class)->where(['id' => $noteEntityRelationData['id']])->delete();
        return $noteEntityRelationData;
    }

    /**
     * 更新note
     * @param $data
     * @return array|bool|null
     * @throws Exception
     * @throws Throwable
     */
    public function updateNote($data)
    {
        $noteModel = model(NoteModel::class);
        $noteModel->setCurrentModuleCode('note');


        $mediaModel = model(MediaModel::class);
        $cosMediaModel = model(CosMediaModel::class);
        $mediaMap = ["media" => $mediaModel, "cos_media" => $cosMediaModel];

        $mediaService = MediaService::getInstance();
        $noteModule = module_by_code('note');

        $noteEntityRelationList = [];
        $addRelationEntityIds = [];
        $addRelationEpisodeCodes = [];
        $projectId = 0;
        $deleteRelationEntityIds = [];
        $deleteRelationEpisodeCodes = [];
        $demandTypeList = [];

        if (isset($data["note_entity_relation"])) {
            $limit = 100;
            //暂时限制为100个
            if (count($data["note_entity_relation"]) > $limit) {
                throw new LogicException('note entity relation sum exceed the limit', ErrorCode::NOTE_ENTITY_RELATION_SUM_EXCEED_THE_LIMIT);
            }
            //首先去重
            foreach ($data["note_entity_relation"] as $noteEntityRelationItem) {
                $noteEntityRelationKey = join("_", [
                    "relation_type" => $noteEntityRelationItem["relation_type"],
                    "relate_module_code" => $noteEntityRelationItem["relate_module_code"],
                    "project_id" => $noteEntityRelationItem["project_id"],
                    "episode_code" => $noteEntityRelationItem["episode_code"],
                    "entity_id" => $noteEntityRelationItem["entity_id"],
                    "task_id" => $noteEntityRelationItem["task_id"],
                ]);
                $noteEntityRelationList[$noteEntityRelationKey] = $noteEntityRelationItem;
            }

            $noteEntityRelationListData = model(NoteEntityRelationModel::class)->where(["note_id" => $data["note"]["id"]])->select();
            $noteEntityRelationListIndexData = [];
            foreach ($noteEntityRelationListData as $noteEntityRelationListDataItem) {
                $noteEntityRelationIndex = join("_", [
                    "relation_type" => $noteEntityRelationListDataItem["relation_type"],
                    "relate_module_code" => $noteEntityRelationListDataItem["relate_module_code"],
                    "project_id" => $noteEntityRelationListDataItem["project_id"],
                    "episode_code" => $noteEntityRelationListDataItem["episode_code"],
                    "entity_id" => $noteEntityRelationListDataItem["entity_id"],
                    "task_id" => $noteEntityRelationListDataItem["task_id"],
                ]);
                $noteEntityRelationListIndexData[$noteEntityRelationIndex] = $noteEntityRelationListDataItem;
            }

            //比较差集 找出不存在的,进行新增,反过来比较进行删除
            $addNoteEntityRelationList = array_diff_key($noteEntityRelationList, $noteEntityRelationListIndexData);
            $deleteNoteEntityRelationList = array_diff_key($noteEntityRelationListIndexData, $noteEntityRelationList);

            foreach ($addNoteEntityRelationList as $addNoteEntityRelationListItem) {
                if (empty($projectId)) {
                    $projectId = $addNoteEntityRelationListItem["project_id"];
                }
                if ($addNoteEntityRelationListItem["relation_type"] === "episode") {
                    $addRelationEpisodeCodes[$addNoteEntityRelationListItem["episode_code"]] = $addNoteEntityRelationListItem["episode_code"];
                } else {
                    $addRelationEntityIds[$addNoteEntityRelationListItem["entity_id"]] = $addNoteEntityRelationListItem["entity_id"];
                }

                $tmp = [
                    "relation_type" => $addNoteEntityRelationListItem['relation_type'],
                    "demand_type" => $data['note']['demand_type'],
                    "module_code" => $addNoteEntityRelationListItem['relate_module_code'],
                ];
                $demandTypeList[join("_", $tmp)] = $tmp;
            }

            // 找到被删除关联关系的实体ids
            foreach ($deleteNoteEntityRelationList as $deleteNoteEntityRelationItem) {
                if (empty($projectId)) {
                    $projectId = $deleteNoteEntityRelationItem["project_id"];
                }
                if ($deleteNoteEntityRelationItem["relation_type"] === "episode") {
                    $deleteRelationEpisodeCodes[$deleteNoteEntityRelationItem["episode_code"]] = $deleteNoteEntityRelationItem["episode_code"];
                } else {
                    $deleteRelationEntityIds[$deleteNoteEntityRelationItem["entity_id"]] = $deleteNoteEntityRelationItem["entity_id"];
                }
            }

            $addRelationEntityIds = array_values($addRelationEntityIds);
            $deleteRelationEntityIds = array_values($deleteRelationEntityIds);
            $episodeCodes = array_merge($addRelationEpisodeCodes, $deleteRelationEpisodeCodes);
            if (!empty($episodeCodes)) {
                $episodeList = model(EntityModel::class)->where(["code" => ["IN", $episodeCodes], "project_id" => $projectId])->field("code,id")->select();
                $episodeList = array_column($episodeList, "id", "code");
                $addRelationEpisodeIds = array_values(array_intersect_key($episodeList, $addRelationEpisodeCodes));
                $deleteRelationEpisodeIds = array_values(array_intersect_key($episodeList, $deleteRelationEpisodeCodes));
                $addRelationEntityIds = array_merge($addRelationEntityIds, $addRelationEpisodeIds);
                $deleteRelationEntityIds = array_merge($deleteRelationEntityIds, $deleteRelationEpisodeIds);
            }
        }

        $noteEntityRelationData = false;
        $deleteNoteEntityRelationData = false;
        $currentModuleLinkRelation = $noteModel->parserFilterModuleRelation(true);

        $noteModel->startTrans();//强制开启事务
        try {
            $relationIds = [];
            foreach ($data as $moduleCode => $item) {
                if (isset($currentModuleLinkRelation[$moduleCode]) && isset($mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']])) {
                    $mediaIds = [];
                    if (is_many_dimension_array($item)) {
                        foreach ($item as $mediaItem) {
                            if (empty($mediaItem)) {
                                continue;
                            }

                            if ($currentModuleLinkRelation[$moduleCode]['module_code'] === "media") {
                                $mediaService->generateMediaData($mediaItem);
                            }

                            // 判定是否需要更新
                            if (isset($mediaItem[$mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getPk()])) {
                                $mediaIds[] = $mediaItem[$mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getPk()];
                                $mediaData = $mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->modifyItem($mediaItem);
                            } else {
                                $mediaData = $mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->addItem($mediaItem);
                            }

                            if (!$mediaData) {
                                // 跳过没有数据更新
                                if ($mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getErrorCode() !== \think\exception\ErrorCode::NO_DATA_HAS_BEEN_CHANGED) {
                                    throw new LogicException($mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getError(), ErrorCode::MEDIA_ADD_FAILURE);
                                }
                            } else {
                                $mediaIds[] = $mediaData['id'];
                            }
                        }
                    }
                    $relationIds[$moduleCode] = join(',', array_unique($mediaIds));
                }
            }

            // 把水平自定义关联数据回插入主表
            foreach ($relationIds as $filed => $relationId) {
                $data['note'][$filed] = $relationId;
            }

            $commonService = CommonService::instance("note");

            $resData = $commonService->commonUpdate(['data' => $data['note']], $noteModule, false);
            if (!empty($addNoteEntityRelationList)) {
                $addNoteEntityRelationList = array_map(function ($noteEntityRelationItem) use ($resData) {
                    $noteEntityRelationItem["note_id"] = $resData["id"];
                    $noteEntityRelationItem["created"] = time();
                    $noteEntityRelationItem["created_by"] = $resData["created_by"];
                    return $noteEntityRelationItem;
                }, $addNoteEntityRelationList);
                $addNoteEntityRelationList = array_values($addNoteEntityRelationList);
                $noteRelationAdded = [
                    'type' => 'add',
                    'changed' => $addNoteEntityRelationList,
                    'operate_at' => time()
                ];
                Hook::listen('note_relation_changed', $noteRelationAdded);
                $noteEntityRelationData = model(NoteEntityRelationModel::class)->addAll($addNoteEntityRelationList);
            }

            if (!empty($deleteNoteEntityRelationList)) {
                $noteRelationDeleted = [
                    'type' => 'deleted',
                    'changed' => $deleteNoteEntityRelationList,
                    'operate_at' => time()
                ];
                Hook::listen('note_relation_changed', $noteRelationDeleted);
                $deleteNoteEntityRelationData = model(NoteEntityRelationModel::class)->where(["id" => ["IN", array_column($deleteNoteEntityRelationList, "id")]])->delete();
            }

            $noteModel->commit();

            // 添加关联关系,消息推送 (找到相应的群)
            if (!empty($noteEntityRelationData)) {
                $data = [
                    "type" => 'add_entity_demand_relation_message',
                    "data" => [
                        "entity_ids" => $addRelationEntityIds,
                        "demand_id" => $resData["id"],
                        "demand_type_list" => array_values($demandTypeList)
                    ]
                ];
                Client::send('send_entity_demand_relation_message', build_queue_data_with_xu($data));
            }

            // 移除关联关系,消息推送 (找到相应的群)
            if (!empty($deleteNoteEntityRelationData)) {
                $data = [
                    "type" => "delete_entity_demand_relation_message",
                    "data" => [
                        "entity_ids" => $deleteRelationEntityIds,
                        "demand_id" => $resData["id"]
                    ]
                ];
                Client::send('send_entity_demand_relation_message', build_queue_data_with_xu($data));
            }

        } catch (Throwable $e) {
            $noteModel->rollback();
            throw $e;
        }

        $resData["created_by"] = model(UserModel::class)->field("id,name,avatar")->find();
        return $resData;
    }

    /**
     * 获取会话的需求列表
     * @param $param
     * @return array|false|mixed|string
     */
    public function getChatDemandList($param)
    {
        $entityId = $param["filter"]["note_entity_relation.entity_id"];
        $entity = model(EntityModel::class)->field("id,project_id,code,module_id")->find($entityId);

        if (empty($entity)) {
            return [];
        }

        if ($entity["module_id"] === module_by_code("episode")["id"]) {
            $param["filter"]["note_entity_relation.project_id"] = $entity["project_id"];
            $param["filter"]["note_entity_relation.episode_code"] = $entity["code"];
            $param["filter"]["note_entity_relation.relate_module_code"] = "shot";
            unset($param["filter"]["note_entity_relation.entity_id"]);
        }
        return $this->getDemandNoteList($param);
    }

    //TODO 取消关联之后,消息点击弹出的侧边的需求应当显示为以取消关联 需要参数(note_id,会话的entity_id)
    // TODO 返修反馈 获取返修的内容

    /**
     * 追加楼层回复信息
     * @param array $res
     * @param $noteQueryFields
     * @return void
     */
    private function appendFloorReplyNoteList(array &$res, $noteQueryFields)
    {
        // 楼层回复查询
        $floorReplyQueryCount = 10;
        $floorNoteList = model(NoteModel::class)
            ->join('module on note.module_id = module.id', 'LEFT')
            ->where(['note.comment_floor_note_id' => ['IN', array_column($res['rows'], 'note_id')]])
            ->where("(select count(1) from note n1 where n1.comment_floor_note_id = note.comment_floor_note_id and n1.id < note.id) < $floorReplyQueryCount")
            ->field(build_complex_query_fields($noteQueryFields, '__'))
            ->select();
        if (empty($floorNoteList)) {
            return;
        }
        $floorNoteList = $this->dealNoteMediaAndUser($floorNoteList, ['id', 'parent_id', 'comment_floor_note_id']);
        $this->appendNoteParent($floorNoteList, $noteQueryFields);
        $floorNoteList = array_group_by($floorNoteList, 'note_comment_floor_note_id');
        foreach ($res['rows'] as &$row) {
            $row['floor_comment'] = $floorNoteList[$row['note_id']] ?? [];
        }
    }

    /**
     * 追加反馈parent信息
     * @param $floorNoteList
     * @param $noteQueryFields
     * @return void
     */
    private function appendNoteParent(&$floorNoteList, $noteQueryFields)
    {
        $parentIdList = array_column($floorNoteList, 'note_parent_id', 'note_parent_id');
        unset($parentIdList[0]);
        $parentIdList = array_values($parentIdList);
        // 回复楼层 回复的具体信息查询
        if (empty($parentIdList)) {
            return;
        }
        $floorReplyToList = model(NoteModel::class)
            ->join('module on note.module_id = module.id', 'LEFT')
            ->where(['note.id' => ['IN', $parentIdList]])
            ->field(build_complex_query_fields($noteQueryFields, '__'))
            ->select();
        $floorReplyToList = $this->dealNoteMediaAndUser($floorReplyToList, ['id', 'parent_id', 'comment_floor_note_id']);
        $floorReplyToList = array_column($floorReplyToList, null, 'note_id');
        foreach ($floorNoteList as &$noteItem) {
            if ($noteItem['note_parent_id']) {
                $noteItem['parent'] = $floorReplyToList[$noteItem['note_parent_id']] ?? null;
            }
        }
    }


    /**
     * 解析需求关联关系成任务筛选器
     * @param array $relations
     * @return array
     * @throws Exception
     */
    public function parseDemandRelation(array $relations)
    {
        $note = model(NoteModel::class)->find(array_values($relations)[0]['note_id']);
        $relationsGrouped = array_group_by($relations, 'relation_type');
        $relationFilter = [];
        foreach ($relationsGrouped as $relationType => $relations) {
            switch ($relationType) {
                case 'episode':
                    $relationFilter[] = [
                        'entity.initial_episode_code' => [
                            'IN', array_unique(array_column($relations, 'episode_code'))
                        ],
                        'entity.module_id' => module_by_code('shot')['id'],
                    ];
                    break;
                case 'entity':
                    $entityList = model(EntityModel::class)
                        ->where(['id' => ['IN', array_unique(array_column($relations, 'entity_id'))]])
                        ->field('id,name,initial_episode_code,showings_number,code,module_id,project_id,json')
                        ->select();
                    if (empty($entityList)) {
                        break;
                    }
                    $entityIdList = EntityService::getInstance()->getEntityIdListScoped($entityList);
                    if (empty($entityIdList)) {
                        break;
                    }
                    $relationFilter[] = [
                        'entity.id' => [
                            'IN', $entityIdList
                        ]
                    ];
                    break;
                case 'step':
                    $relationFilter[] = [
                        'task.id' => [
                            'IN', array_unique(array_column($relations, 'task_id'))
                        ]
                    ];
                    break;
            }
        }
        if (empty($relationFilter)) {
            return null;
        }
        $relationFilter['_logic'] = 'or';
        $taskFilter = [
            'entity.project_id' => $note['project_id'],
            'task.step_type' => 'step',
        ];
        $taskFilter[] = $relationFilter;
        return $taskFilter;
    }

    /**
     * 查询需求关联的任务信息
     * @param $taskFilter
     * @return array|false|mixed|string
     */
    public function getDemandRelateTask($taskFilter)
    {
        $fields = [
            "task.id", "concat(entity.initial_episode_code,'/',entity.showings_number,'/',entity.name,'/',task.name) as task_name",
            "task.assignee_producer", "task.department_id", "task.executor", "task.executor_type", "task.executor_tenant_id",
            "task.entity_id", "entity.initial_episode_code", "entity.showings_number", "entity.name as entity_name",
            "task.name as step_name", "entity.tenant_id", "entity.project_id", "entity.module_id",
        ];
        return model(EntityModel::class)
            ->join('task on task.entity_id = entity.id')
            ->where($taskFilter)
            ->field($fields)
            ->select();
    }

    /**
     * 解析需求关联的任务数据
     * @param array $relationEntityTaskList
     * @return array
     */
    private function parseDemandRelatedTaskData(array $relationEntityTaskList, $withHeadman = true)
    {
        $notifyRelationListGrouped = [];
        foreach ($relationEntityTaskList as $relationEntityTaskItem) {
            $notifyRelationListGrouped[$relationEntityTaskItem['executor_type']][] = $relationEntityTaskItem;
        }

        $userIdList = [];
        $departmentIdList = [];
        $tenantIdList = [];
        foreach ($notifyRelationListGrouped as $executorType => $notifyRelationList) {
            if ($executorType === 'outside') {
                // 外包任务 需要查询外包对应的乙方任务
                $notifyRelationList = model(OrderTaskListModel::class)
                    ->alias('otl')
                    ->join('task t on t.id = otl.real_task_id')
                    ->join('entity e on e.id = t.entity_id')
                    ->where(['otl.task_id' => ['IN', array_column($notifyRelationList, 'id')]])
                    ->where(['otl.status' => 'default'])
                    ->field("concat(e.initial_episode_code,'/',e.showings_number,'/',e.name,'/',t.name) as task_name,
                    otl.real_task_id as id,t.project_id,t.assignee_producer,t.executor_tenant_id,t.executor,t.department_id,
                    t.entity_id,e.initial_episode_code,e.showings_number,e.name as entity_name,t.name as step_name,e.tenant_id,e.module_id")
                    ->select();
                $notifyRelationListGrouped[$executorType] = $notifyRelationList;
                if (empty($notifyRelationList)) {
                    continue;
                }
                $outsideProjectIdList = array_column($notifyRelationList, 'project_id', 'project_id');
                $outsideProjectBusinessProducer = model(ProjectMemberModel::class)
                    ->where([
                        'project_id' => ['IN', array_values($outsideProjectIdList)],
                        'role_code' => TaskService::BUSINESS_PRODUCER_CODE,
                    ])
                    ->group('project_id,user_id')
                    ->field('project_id,user_id')
                    ->select();
                $outsideProjectBusinessProducerListGrouped = [];
                foreach ($outsideProjectBusinessProducer as $outsideProjectBusinessProducerItem) {
                    $outsideProjectBusinessProducerListGrouped[$outsideProjectBusinessProducerItem['project_id']][] = $outsideProjectBusinessProducerItem['user_id'];
                }
                array_push($userIdList, ...array_column($outsideProjectBusinessProducer, 'user_id', 'user_id'));
                foreach ($notifyRelationList as &$notifyRelationItem) {
                    $notifyRelationItem['project_business_producer'] = $outsideProjectBusinessProducerListGrouped[$notifyRelationItem['project_id']] ?? [];
                }
            }
            array_push($userIdList, ...array_column($notifyRelationList, 'executor', 'executor'));
            array_push($departmentIdList, ...array_column($notifyRelationList, 'department_id', 'department_id'));
            array_push($userIdList, ...array_column($notifyRelationList, 'assignee_producer', 'assignee_producer'));
            array_push($tenantIdList, ...array_column($notifyRelationList, 'executor_tenant_id', 'executor_tenant_id'));
        }
        $departmentIdList = array_filter(array_unique($departmentIdList));
        $tenantIdList = array_filter(array_unique($tenantIdList));
        $userDepartmentMap = [];
        // easyMode 不查部门组长
        if (!empty($departmentIdList) && $withHeadman) {
            $filter = [
                'tenant_id' => ['-in', implode(',', $tenantIdList)],
                'department_id' => ['-in', implode(',', $departmentIdList)],
                'role_code' => TaskService::HEADMAN_CODE,
            ];
            $userDepartmentMap = UserService::getInstance()->getDepartmentSaasUserMap($filter);
        }
        $userIdList = array_filter(array_unique($userIdList));
        $userMap = [];
        if (!empty($userIdList)) {
            $userMap = UserService::getInstance()->selectLocalUserList(['id' => ['IN', implode(',', $userIdList)]], ['id,union_id']);
            $userMap = array_column($userMap, null, 'id');
        }
        $res = [];
        foreach ($notifyRelationListGrouped as $notifyRelationList) {
            foreach ($notifyRelationList as $notifyRelationItem) {
                $notifyRelationItem['notify_union_id_list'] = [];
                if ($notifyRelationItem['executor'] > 0 && isset($userMap[$notifyRelationItem['executor']])) {
                    $notifyRelationItem['notify_union_id_list'][] = $userMap[$notifyRelationItem['executor']]['union_id'];
                }
                if ($notifyRelationItem['assignee_producer'] > 0 && isset($userMap[$notifyRelationItem['assignee_producer']])) {
                    $notifyRelationItem['notify_union_id_list'][] = $userMap[$notifyRelationItem['assignee_producer']]['union_id'];
                }
                if ($notifyRelationItem['department_id'] > 0 && isset($userDepartmentMap[$notifyRelationItem['department_id']])) {
                    foreach (array_column($userDepartmentMap[$notifyRelationItem['department_id']], 'union_id') as $unionId) {
                        $notifyRelationItem['notify_union_id_list'][] = $unionId;
                    }
                }
                if (!empty($notifyRelationItem['project_business_producer'])) {
                    foreach ($notifyRelationItem['project_business_producer'] as $projectBusinessProducer) {
                        if (!empty($userMap[$projectBusinessProducer])) {
                            $notifyRelationItem['notify_union_id_list'][] = $userMap[$projectBusinessProducer]['union_id'];
                        }
                    }
                }
                $notifyRelationItem['notify_union_id_list'] = array_filter(array_unique($notifyRelationItem['notify_union_id_list']));
                $res[] = $notifyRelationItem;
            }
        }
        return $res;
    }

    /**
     * 查询需求指定关联关系的任务和人员
     * @param $relations
     * @return array
     * @throws Exception
     */
    public function getDemandSpecifiedRelationTasksAndPeople($relations, $withHeadman = true)
    {
        $taskFilter = $this->parseDemandRelation($relations);
        if (empty($taskFilter)) {
            return [];
        }
        $relationEntityTaskList = $this->getDemandRelateTask($taskFilter);
        if (empty($relationEntityTaskList)) {
            return [];
        }
        return $this->parseDemandRelatedTaskData($relationEntityTaskList, $withHeadman);
    }

    /**
     * 查询需求指定关联关系的任务和人员
     * @param $relations
     * @return array
     * @throws Exception
     */
    public function getDemandSpecifiedRelationPeople($relations, $withHeadman = true)
    {
        $taskFilter = $this->parseDemandRelation($relations);
        if (empty($taskFilter)) {
            return [];
        }
        $relationEntityTaskList = $this->getDemandRelateTask($taskFilter);
        if (empty($relationEntityTaskList)) {
            return [];
        }
        $relatedTaskAndPeople = $this->parseDemandRelatedTaskData($relationEntityTaskList, $withHeadman);
        $res = [];
        foreach ($relatedTaskAndPeople as $relatedTaskAndPeopleItem) {
            $res = array_merge($res, $relatedTaskAndPeopleItem['notify_union_id_list']);
        }
        return array_unique($res);
    }

    /**
     * 查询需求关联任务相关人员
     * @param $note
     * @return array
     * @throws Exception
     */
    public function getDemandRelatePeopleAndTask($note)
    {
        $relations = model(NoteEntityRelationModel::class)->where(['note_id' => $note['id']])->select();
        return $this->getDemandSpecifiedRelationTasksAndPeople($relations);
    }

    /**
     * 查询需求相关人员
     * @param $note
     * @return array
     * @throws Exception
     */
    public function getDemandRelatePeople($note, $withHeadman = true)
    {
        $relations = model(NoteEntityRelationModel::class)->where(['note_id' => $note['id']])->select();
        return $this->getDemandSpecifiedRelationPeople($relations, $withHeadman);
    }

    /**
     * 获得需求评论模板id
     * @return int
     */
    public function getDemandCommentAddTemplateId()
    {
        $messageTemplateId = OptionsService::getInstance()->getOptionsConfigItemData('message_notify_template_config', 'demand_add_comment_template_id');
        return (int)$messageTemplateId;
    }

    /**
     * 获得新需求模板id
     * @return int
     */
    public function getDemandChangedRelationTemplateId()
    {
        $messageTemplateId = OptionsService::getInstance()->getOptionsConfigItemData('message_notify_template_config', 'demand_add_template_id');
        return (int)$messageTemplateId;
    }

    /**
     * 获取任务的需求列表
     * @param $param
     * @return array|false|mixed|string
     */
    public function getTaskDemandNoteList($param)
    {
        $taskId = $param["filter"]["task_id"];
        unset($param["filter"]["task_id"]);

        $entityFields = [
            "entity.id",
            "entity.initial_episode_code",
            "entity.showings_number",
            "entity.module_id",
            "entity.project_id",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_design'))) as design",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.shot_storyboard')) as shot_storyboard",
        ];

        $task = model(TaskModel::class)->field("id,step_type,project_id,entity_id,step_category_id,step_id")->find($taskId);
        if ($task['step_type'] == TaskModel::STEP_TYPE_STEP) {

            $entity = model(EntityModel::class)
                ->join("module on module.id = entity.module_id")->where(["entity.id" => $task["entity_id"]])->field($entityFields)->find();

            //判定是否是外包
//        $project = model(ProjectModel::class)->field("id,belong_project_id,type")->find($entity["project_id"]);

            //如果是外包替换entity
            $partyATask = model(OrderTaskListModel::class)->where(['real_task_id' => $taskId, 'status' => OrderTaskListModel::STATUS_DEFAULT])->field("task_id")->find();
            $partyBTask = model(OrderTaskListModel::class)->where(['task_id' => $taskId, 'status' => OrderTaskListModel::STATUS_DEFAULT])->field("real_task_id")->find();

            $entityList = [["entity" => $entity, 'project_id' => $entity['project_id'], 'showings_number' => $entity['showings_number'], 'initial_episode_code' => $entity['initial_episode_code'], "task_id" => $taskId, 'step_category_id' => $task['step_category_id']]];
            $noteFilter = [];
            if (!empty($partyATask["task_id"])) {
                $partyAEntity = model(TaskModel::class)
                    ->join("entity on entity.id = task.entity_id")
                    ->join("module on module.id = entity.module_id")
                    ->where(['task.id' => $partyATask["task_id"]])->field($entityFields)->find();
                $partyATaskId = $partyATask["task_id"];
                $entityList[] = ["entity" => $partyAEntity, 'initial_episode_code' => $entity['initial_episode_code'], 'showings_number' => $entity['showings_number'], 'project_id' => $partyAEntity['project_id'], "task_id" => $partyATaskId, 'step_category_id' => $task['step_category_id']];
            }
            if (!empty($partyBTask["real_task_id"])) {
                $partyBEntity = model(TaskModel::class)
                    ->join("entity on entity.id = task.entity_id")
                    ->join("module on module.id = entity.module_id")
                    ->where(['task.id' => $partyBTask["real_task_id"]])->field($entityFields)->find();
                $partyBTaskId = $partyBTask["real_task_id"];
                $entityList[] = ["entity" => $partyBEntity, 'initial_episode_code' => $entity['initial_episode_code'], 'showings_number' => $entity['showings_number'], 'project_id' => $partyBEntity['project_id'], "task_id" => $partyBTaskId, 'step_category_id' => $task['step_category_id']];
            }
            if (empty($entityList)) {
                return [];
            }
            $entityProjectIds = [];
            foreach ($entityList as $entityItem) {
                $entityProjectIds[] = $entityItem['project_id'];
                $taskFilter = [];
                $entity = $entityItem["entity"];
                $entityId = $entity["id"];
                $taskId = $entityItem["task_id"];

                $episodeFilter = [];
                $currentEntityModuleCode = module_by_id($entity["module_id"])["code"]; //根据module_id找到code
                if (in_array($currentEntityModuleCode, ["asset", "level"])) {
                    //替换为概念id
                    $entityId = $entity["design"];
                } elseif (in_array($currentEntityModuleCode, ["shot", "storyboard"])) {
                    $episodeFilter = [
                        "note_entity_relation.episode_code" => $entity["initial_episode_code"],
                        "note_entity_relation.relate_module_code" => "shot",
                        "note_entity_relation.relation_type" => "episode"
                    ];
                    if (in_array($currentEntityModuleCode, ["shot"])) {
                        $entityId = $entity["shot_storyboard"];
                    }
                }

                $taskFilter[] = [
                    "note_entity_relation.entity_id" => $entityId,
                    "note_entity_relation.relation_type" => "entity"
                ];
                $taskFilter[] = [
                    "note_entity_relation.entity_id" => $entityId,
                    "note_entity_relation.relation_type" => "step_category",
                    "note_entity_relation.step_category_id" => $entityItem['step_category_id'],
                    "note_entity_relation.repair_accept_status" => ['NEQ', NoteEntityRelationModel::REPAIR_ACCEPT_STATUS_NO], // 对返修不接受的反馈不显示在任务卡片上
                ];
                $taskFilter[] = [
                    "note_entity_relation.project_id" => $entityItem['project_id'],
                    "note_entity_relation.relation_type" => "scene",
                    'note_entity_relation.episode_code' => $entityItem['initial_episode_code'],
                    'note_entity_relation.scene' => $entityItem['showings_number'],
                ];
                $taskFilter[] = [
                    "note_entity_relation.task_id" => $taskId,
                    "note_entity_relation.relation_type" => "step"
                ];

                if (!empty($episodeFilter)) {
                    $taskFilter[] = $episodeFilter;
                }
                $taskFilter["_logic"] = "OR";
                $noteFilter[] = [
                    $taskFilter
                ];

            }
        } else {
            $taskFilter = [
                "note_entity_relation.task_id" => $taskId,
                "note_entity_relation.relation_type" => "todo"
            ];
            $noteFilter[] = $taskFilter;
        }

        $noteFilter["_logic"] = "OR";
        $filter[] = $noteFilter;
        $filter[] = [
            ['note.review_feedback_id' => 0],
            ['note.review_feedback_id' => ['GT', 0], 'note.demand_status' => ['IN', NoteModel::NOTE_DEMAND_STATUS_ACCEPTED]],
            '_logic' => 'or',
        ];
        // 搜集 , 搜实体, 搜当前工序
        $param["filter"] = array_merge($filter, $param["filter"]);

        if (!empty($entityProjectIds)) {
            $param["filter"]['entity_project_ids'] = $entityProjectIds;
        }

        return $this->getDemandNoteList($param);
    }

    /**
     * 获取任务的需求列表
     * @param $param
     * @return array
     */
    public function getEntityDemandNoteList($param)
    {
        $entityId = $param["filter"]["entity_id"];
        unset($param["filter"]["entity_id"]);

        $entityFields = [
            "entity.id",
            "entity.code",
            "entity.module_id",
            "entity.project_id",
            "entity.initial_episode_code",
            "entity.showings_number",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_design'))) as design",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.shot_storyboard')) as shot_storyboard",
        ];

        $entity = model(EntityModel::class)
            ->join("module on module.id = entity.module_id")
            ->where(["entity.id" => $entityId])->field($entityFields)->find();

        if (empty($entity)) {
            return [];
        }

        //判定是否是外包
        $project = model(ProjectModel::class)->field("id,belong_project_id,type")->find($entity["project_id"]);

        //如果是外包替换entity
        if ($project["type"] === "outsource") {
            $entity = model(EntityModel::class)
                ->join("module on module.id = entity.module_id")
                ->where([
                    "entity.project_id" => $project["belong_project_id"],
                    "entity.initial_episode_code" => $entity["initial_episode_code"],
                    "entity.showings_number" => $entity["showings_number"],
                    "entity.module_id" => $entity["module_id"],
                    "entity.code" => $entity["code"]
                ])->field($entityFields)->find();
        }

        if (empty($entity)) {
            return [];
        }

        $entityId = $entity["id"];
        $taskFilter = [];
        $currentEntityModuleCode = module_by_id($entity["module_id"])["code"];
        if (in_array($currentEntityModuleCode, ["asset", "level"])) {
            //替换为概念id
            $entityId = $entity["design"];
        } elseif (in_array($currentEntityModuleCode, ["shot", "storyboard"])) {
            $taskFilter[] = [
                "note_entity_relation.episode_code" => $entity["initial_episode_code"],
                "note_entity_relation.relate_module_code" => "shot",
                "note_entity_relation.relation_type" => "episode"
            ];

            $taskFilter[] = [
                "note_entity_relation.project_id" => $entity["project_id"],
                "note_entity_relation.episode_code" => $entity["initial_episode_code"],
                "note_entity_relation.scene" => $entity["showings_number"],
                "note_entity_relation.relate_module_code" => "shot",
                "note_entity_relation.relation_type" => "scene"
            ];

            //替换为分镜id
            if (in_array($currentEntityModuleCode, ["shot"])) {
                $entityId = $entity["shot_storyboard"];
            }
        }

        $taskFilter[] = [
            "note_entity_relation.entity_id" => $entityId,
            "note_entity_relation.relation_type" => "entity"
        ];

        $taskFilter["_logic"] = "OR";

        $filter[] = [
            "note.project_id" => $entity["project_id"],
            $taskFilter
        ];

        // 搜集 , 搜实体, 搜当前工序
        $param["filter"] = array_merge($filter, $param["filter"]);
        return $this->getDemandNoteList($param);
    }

    /**
     * 生成版本基础的筛选条件
     * @param $tenantId
     * @param $task
     * @param $version
     * @return array
     */
    private function buildBaseVersionFilter($tenantId, $task, $version)
    {
        $filter = [
            "note.demand_type" => NoteModel::DEMAND_TYPE_REVIEW_FEEDBACK,
            "note_entity_relation.relation_type" => "step",
            "note_entity_relation.version_id" => $version['id'],
        ];
        // 任务为内部制作
        if (!in_array($task['executor_type'], TaskModel::OUTSIDE_EXECUTE_TYPE_GROUP)) {
            $orderPartyATask = OrderService::getInstance()->getOrderTaskListItemByRealTaskId($version["link_id"]);
            // 判定是否被外包没有
            if (empty($orderPartyATask)) {
                return [$filter, $task, $version];
            }

            $partyATask = model(TaskModel::class)->field("tenant_id,project_id")->find($orderPartyATask["task_id"]);
            if (empty($partyATask)) {
                return [$filter, $task, $version];
            }
            //判定当前进入是否在甲方还是乙方,相同则在乙方
            if ($version["tenant_id"] == $tenantId) {
                //乙方
                $filter = [
                    $filter,
                    [
                        "note.demand_type" => NoteModel::DEMAND_TYPE_REVIEW_FEEDBACK,
                        "note_entity_relation.relation_type" => "step",
                        "note_entity_relation.version_id" => $version['id'],
                    ],
                    "_logic" => "OR"
                ];
            }
            return [$filter, $task, $version];
        }

        // 任务外部制作
        $realTask = OrderService::getInstance()->getOutsideTaskByTaskId($task['id']);
        if (empty($realTask)) {
            return [$filter, $task, $version];
        }
        $realVersion = model(VersionModel::class)->where(['module_id' => module_by_code('task')['id'], 'link_id' => $realTask['id'], 'number' => $version['number']])->find();
        if (empty($realVersion)) {
            return [$filter, $task, $version];
        }
        return [[
            $filter,
            [
                "note.demand_type" => NoteModel::DEMAND_TYPE_REVIEW_FEEDBACK,
                "note_entity_relation.relation_type" => "step",
                "note_entity_relation.version_id" => $realVersion['id'],
            ],
            "_logic" => "OR"
        ], $realTask, $realVersion];
    }

    /**
     * 自行补齐参数
     * @param $data
     * @return mixed
     */
    public function autoCompleteRelationNoteEntityRelationParam($data)
    {
        $keyData = [];
        $countDiff = 0;
        foreach ($data as $dataItem) {
            if (empty($keyData)) {
                $keyData = $dataItem;
            } else {
                $isMoreKey = !empty(array_diff_key($dataItem, $keyData));
                $isLessKey = !empty(array_diff_key($keyData, $dataItem));
                if ($isMoreKey) {
                    $keyData = array_merge($keyData, $dataItem);
                }
                if (empty($countDiff) && ($isMoreKey || $isLessKey)) {
                    $countDiff = 1;
                }
            }
        }

        //如果有需要补齐的
        if (!empty($countDiff)) {
            //默认值补充
            foreach ($keyData as $keyDataIndex => $keyDataItem) {
                if (is_numeric($keyDataItem)) {
                    $keyData[$keyDataIndex] = 0;
                } elseif (is_string($keyDataItem)) {
                    $keyData[$keyDataIndex] = "";
                }
            }

            foreach ($data as $dataIndex => $dataItem) {
                $diffData = array_diff_key($keyData, $dataItem);
                if (!empty($diffData)) {
                    $data[$dataIndex] = array_merge($dataItem, $diffData);
                }
            }
        }
        return $data;
    }

    /**
     * 通过概念id获取资产id
     * @param $designId
     * @param $projectId
     * @return array
     */
    public function getAssetDesignInfo($designId, $projectId)
    {
        $assetId = module_by_code('asset')['id'];
        $levelId = module_by_code('level')['id'];

        $where['module_id'] = ['IN', [$assetId, $levelId]];
        $where['project_id'] = $projectId;
        $where["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_design')))"] = $designId;

        $entityData = model(EntityModel::class)->join('module on entity.module_id = module.id')->where($where)->field('entity.id')->find();

        if (empty($entityData)) {
            return [];
        }
        $entityArr = [$designId];
        $entityId = $entityData['id'];
        return array_merge($entityArr, [$entityId]);
    }

    /**
     * 获取镜头下的分镜
     * @param $shotId
     * @param $projectId
     * @return []|array
     */
    public function getShotStoryboardInfo($shotId, $projectId)
    {
        $shotId = (int)$shotId;
        $where['module_id'] = module_by_code('shot')['id'];
        $where['project_id'] = $projectId;
        $where["JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.shot_storyboard'))"] = $shotId;
        $entityData = model(EntityModel::class)
            ->field('id,json,project_id,module_id')
            ->where($where)
            ->find();
        $entityArr = [$shotId];
        if (empty($entityData)) {
            return [];
        }
        return array_merge($entityArr, [$entityData['id']]);
    }

    /**
     * 获取需求列表
     * @param $param
     * @param $page
     * @param $moduleCode
     * @return array
     * @throws Exception|\Exception
     */
    public function getList($param, $page, $moduleCode)
    {
        $stepCategoryIdList = $param['category_id_list'] ?? [];
        $yearMonth = $param['year_month'] ?? 0;
        $projectId = $param['project_id'] ?? 0;
        if (!empty($stepCategoryIdList) && !empty($yearMonth) && !empty($projectId) && empty($param['initial_episode_code_list'])) {
            $episodeIdList = $this->getMonthStepCategoryEpisodeList($stepCategoryIdList, $yearMonth, $projectId);
            if (empty($episodeIdList)) {
                return [];
            }
            $episodeCodeList = model(EntityModel::class)->where(['id' => ['IN', $episodeIdList]])->field('code as initial_episode_code')->select();
            if (empty($episodeCodeList)) {
                return [];
            }
            $param['initial_episode_code_list'] = $episodeCodeList;
        }

        $filter = $this->getNoteManageListFilter($param, $moduleCode); // 需求列表过滤条件处理
        if (empty($filter)) {
            return [];
        }
        $statusList = model(StatusModel::class)->select();
        $statusMap = array_column($statusList, null, 'id');
        $entityFields = [
            'entity.id',
            'entity.code',
            'entity.module_id',
            'entity.project_id',
            'entity.name',
            'entity.showings_number',
            'entity.initial_episode_code',
            'entity.plan_end_time',
            'entity.is_append',
            'entity.end_time',
            'entity.remark',
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_status'))) as status",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.design_grade')) as design_grade",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.design_category')) as design_category",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.design_relate_level')) as design_relate_level",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.level_relate_asset')) as level_relate_asset",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.storyboard_media')) as storyboard_media",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.storyboard_asset')) as storyboard_asset",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.storyboard_level')) as storyboard_level",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.storyboard_estimate_duration')) as storyboard_estimate_duration",
        ];

        $entityList = model(EntityModel::class)
            ->join("module on module.id = entity.module_id")
            ->where($filter)
            ->field($entityFields)
            ->page(...$page)
            ->order('entity.id desc')
            ->select();
        $totalNum = model(EntityModel::class)
            ->join("module on module.id = entity.module_id")
            ->where($filter)
            ->count();

        $projectId = $entityList[0]['project_id'] ?? 0;
        $needColumn = $param['need_column'] ?? null;
        $columnData = $this->getNoteColumn($moduleCode, $needColumn);
        $columnMap = array_column($columnData, 'code');
        if (empty($entityList)) {
            return ['column' => $columnData, 'list' => []];
        }
        $entityIds = join(',', array_column($entityList, 'id'));

        // 查询资产需求
        $versionMap = [];
        $noteAssetMap = [];
        $stepResultMap = [];
        $entityNoteCodeMap = [];
        $maxVersionIdList = [];
        $this->appendEntityUpstreamEntityId($entityList);
        if (in_array('asset_note', $columnMap)) {

            $noteAssetMap = $this->getAssetNote($entityIds, false); // 查询资产需求
            $entityNoteCodeMap = $this->getStepNote($entityIds); // 查询环节需求信息

            $stepResultList = $this->getStepResultList($entityList); // 查询 entity 环节
            $stepResultMap = array_group_by($stepResultList, 'entity_id');

            // 环节成果
            $stepResultIds = array_column($stepResultList, 'task_id');
            if (!empty($stepResultIds)) {
                $maxVersionParam = [
                    'filter' => [
                        'module_id' => module_by_code('task')['id'],
                        'link_id' => ['IN', $stepResultIds],
                        'is_deprecate' => 'no',
                    ],
                    'fields' => 'max(id) as id',
                    'group' => 'link_id'
                ];
                $maxVersionList = VersionService::getInstance()->getManyLatestVersion($maxVersionParam);
                if (!empty($maxVersionList)) {
                    $maxVersionIdList = array_column($maxVersionList, 'id');
                    $versionFields = 'user.name,version.link_id,version.id,version.number,version.description,version.created_by,';
                    $versionFields .= 'version.created,version.param,version.is_pass,version.is_publish,media.id,media.thumb,media.param';
                    $versionParam = [
                        'filter' => [
                            'version.id' => ['IN', join(',', $maxVersionIdList)],
                        ],
                        'fields' => $versionFields
                    ];
                    $versionList = model(VersionModel::class)->selectData($versionParam);
                    foreach ($versionList['rows'] ?? [] as $item) {
                        $versionMap[$item['version']['link_id']] = $item;
                    }
                }
            }
        }

        // 查询剧集初始集的id
        $initialEpisodeCode = join(',', array_unique(array_column($entityList, 'initial_episode_code')));
        $entityEpisodeRelationList = model(EntityModel::class)
            ->where(['code' => ['IN', $initialEpisodeCode]])
            ->where(['project_id' => $projectId, 'module_id' => module_by_code('episode')['id']])
            ->field('id,code')
            ->select();
        $entityEpisodeRelationMap = array_column($entityEpisodeRelationList, null, 'code');

        // 查询分镜视频
        $storyboardMediaMap = [];
        if ($moduleCode == 'storyboard') {
            $mediaFields = 'media.id,media.param';
            $storyboardMediaIds = join(',', array_column($entityList, 'storyboard_media'));
            $storyboardMediaIds = trim($storyboardMediaIds, ",");
            if (!empty($storyboardMediaIds)) {
                $mediaList = model(MediaModel::class)->where(['id' => ['IN', $storyboardMediaIds]])->field($mediaFields)->select();
                $mediaList = array_map(function ($item) { // 格式化数据
                    return CommonService::getFormatAttrData(model(MediaModel::class), $item);
                }, $mediaList);
                $storyboardMediaMap = array_column($mediaList, null, 'id');
            }
        }

        // 查询审核任务参数
        $reviewList = [];
        if (!empty($maxVersionIdList)) {
            $reviewList = $this->getReviewData($maxVersionIdList);
            $reviewList = array_column($reviewList, null, 'task_id');
        }

        $list = $this->noteReturnList($entityList, $statusMap, $entityNoteCodeMap, $noteAssetMap, $versionMap, $stepResultMap, $entityEpisodeRelationMap, $storyboardMediaMap, $reviewList);
        return ['column' => $columnData, 'list' => $list, 'total_num' => $totalNum];
    }

    /**
     * 查询剧集下的镜头数量
     * @param $param
     * @param $page
     * @return array
     */
    public function getEpisodeShot($param, $page)
    {
        $entityId = $param['entity_id'] ?? 0;
        // [角色,道具,关卡] 概念id,转换成资产
        $rolePropsLevelCodeData = model(EntityModel::class)
            ->join('module on entity.module_id = module.id')
            ->where(['entity.id' => $entityId])->field("project_id,entity.code,module_id,JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.', module.code, '_category'))) as category")
            ->find();
        if (empty($rolePropsLevelCodeData)) {
            return [];
        }
        $projectId = $rolePropsLevelCodeData['project_id'];
        $rolePropsLevelCode = $rolePropsLevelCodeData['code'];
        // 关卡
        if ($rolePropsLevelCodeData['category'] == '关卡') {
            $levelData = model(EntityModel::class)
                ->where([
                    'project_id' => $projectId,
                    'module_id' => module_by_code('level')['id'],
                    'code' => $rolePropsLevelCode
                ])
                ->field('id')
                ->find();
            if (empty($levelData)) {
                return [];
            }
            $levelId = $levelData['id'];
            $episodeShotCountList = model(EntityModel::class)
                ->where(['project_id' => $projectId, 'module_id' => module_by_code('storyboard')['id']])
                ->where("find_in_set({$levelId}, json ->> '$.storyboard_level')")
                ->page($page[0], $page[1])
                ->field("id,initial_episode_code as code,count(id) as num")
                ->group('initial_episode_code')
                ->select();
            return array_map(function ($tmp) {
                $tmp['source'] = [
                    '关联'
                ];
                $tmp['shot_num'] = $tmp['num'];
                return $tmp;
            }, $episodeShotCountList);
        }

        // 资产id
        $assetId = model(EntityModel::class)->where(['project_id' => $projectId, 'module_id' => module_by_code('asset')['id'], 'code' => $rolePropsLevelCode])->field('id')->find();
        if (empty($assetId)) {
            return [];
        }
        $assetId = $assetId['id'];
        // 查询当前项目下所有的剧集
        $initialEpisodeCode = model(EntityModel::class)
            ->where(['project_id' => $projectId, 'module_id' => module_by_code('episode')['id']])
            ->column('code');
        if (empty($initialEpisodeCode)) {
            return [];
        }
        // 查询剧集下镜头的数量
        $episodeShotCountList = model(EntityModel::class)
            ->where(['project_id' => $projectId, 'module_id' => module_by_code('storyboard')['id'], 'initial_episode_code' => ['IN', $initialEpisodeCode]])
            ->where("find_in_set({$assetId}, json ->> '$.storyboard_asset')")
            ->page($page[0], $page[1])
            ->field("id,code,initial_episode_code as code,json ->> '$.storyboard_asset' as asset_ids,count(id) as num")
            ->group('initial_episode_code')
            ->select();
        if (empty($episodeShotCountList)) {
            return [];
        }
        return array_map(function ($tmp) {
            $tmp['source'] = [
                '关联'
            ];
            $tmp['shot_num'] = $tmp['num'];
            return $tmp;
        }, $episodeShotCountList);
    }

    /**
     * 查询资产需求
     * @param $noteEntityRelationEntityIds
     * @param $isList
     * @return array
     */
    public function getAssetNote($noteEntityRelationEntityIds, $isList)
    {

        $noteEntityRelationData = model(NoteEntityRelationModel::class)
            ->where(['relation_type' => 'entity', 'entity_id' => ['IN', $noteEntityRelationEntityIds]])
            ->field('max(note_id) as note_id,entity_id')->group('entity_id')
            ->select();
        if (empty($noteEntityRelationData)) {
            return [];
        }

        $noteIds = array_column($noteEntityRelationData, 'note_id');
        $noteData = model(NoteModel::class)->where(['id' => ['IN', $noteIds]])->field('id,text')->select();
        if (empty($noteData)) {
            return [];
        }

        $noteMap = array_column($noteData, null, 'id');
        $noteEntityRelationData = array_map(function ($tmp) use ($noteMap) {
            $tmp['text'] = $noteMap[$tmp['note_id']]['text'] ?? null;
            return $tmp;
        }, $noteEntityRelationData);
        if ($isList) {
            return $noteEntityRelationData;
        }
        return array_column($noteEntityRelationData, null, 'entity_id');
    }


    /**
     * 查询环节需求信息
     * @param $noteEntityRelationEntityIds
     * @return array
     */
    public function getStepNote($noteEntityRelationEntityIds)
    {

        $stepNoteList = model(NoteEntityRelationModel::class)
            ->where(['relation_type' => 'step_category', 'entity_id' => ['IN', $noteEntityRelationEntityIds]])
            ->field('max(note_id) as note_id,task_id,entity_id,step_category_id')
            ->group('entity_id,step_category_id desc')
            ->select();
        if (empty($stepNoteList)) {
            return [];
        }

        $noteIds = array_column($stepNoteList, 'note_id');
        $stepCategoryIds = array_unique(array_column($stepNoteList, 'step_category_id'));
        $stepCategoryData = model(StepCategoryModel::class)->where(['id' => ['IN', $stepCategoryIds]])->field('id,name,code')->select();
        if (empty($stepCategoryData)) {
            return [];
        }

        $noteData = model(NoteModel::class)->where(['id' => ['IN', $noteIds]])->field('id,text')->select();
        if (empty($noteData)) {
            return [];
        }

        $noteMap = array_column($noteData, null, 'id');
        $stepCategoryMap = array_column($stepCategoryData, null, 'id');
        $stepNoteList = array_map(function ($tmp) use ($stepCategoryMap, $noteMap) {
            $tmp['code'] = $stepCategoryMap[$tmp['step_category_id']]['code'] ?? null;
            $tmp['text'] = $noteMap[$tmp['note_id']]['text'] ?? null;
            return $tmp;
        }, $stepNoteList);

        $entityNoteCodeMap = [];
        $entityNoteMap = array_group_by($stepNoteList, 'entity_id');
        foreach ($entityNoteMap as $entityNoteKey => $entityNoteItem) {
            $codeNoteItem = array_column($entityNoteItem, null, 'code');
            $entityNoteCodeMap[$entityNoteKey] = $codeNoteItem;
        }
        return $entityNoteCodeMap;
    }

    /**
     * 查询审核任务信息
     * @param $maxVersionIdList
     * @return array|false|mixed|string
     */
    public function getReviewData($maxVersionIdList)
    {
        $taskModuleData = Module::$moduleDictData['module_index_by_code']['task'];
        $versionModuleData = Module::$moduleDictData['module_index_by_code']['version'];
        return model(ReviewRequestModel::class)
            ->join("review_request_node on review_request.id = review_request_node.review_request_id")
            ->join("version v on v.module_id = {$taskModuleData['id']} and v.id = review_request.link_id and review_request.module_id = {$versionModuleData['id']}")
            ->where(['v.id' => ['in', $maxVersionIdList]])
            ->field('v.link_id as task_id,v.id as version_id,review_request_id,review_request_node.id as review_request_node_id')
            ->select();
    }

    /**
     * 需求列表返回数据
     * @param $entity
     * @param $statusMap
     * @param $entityNoteCodeMap
     * @param $noteAssetMap
     * @param $versionMap
     * @param $stepResultMap
     * @param $entityEpisodeRelationMap
     * @param $storyboardMediaMap
     * @param $reviewList
     * @return mixed
     */
    public function noteReturnList($entity, $statusMap, $entityNoteCodeMap, $noteAssetMap, $versionMap, $stepResultMap, $entityEpisodeRelationMap, $storyboardMediaMap, $reviewList)
    {
        $taskStartedStatusList = [StatusService::getInstance()->getTaskDefaultStartStatus()['id'], StatusService::getInstance()->getTaskDoneStatus()['id']];
        foreach ($entity as &$tmp) {
            $statusCodeMap = array_column($statusMap, null, 'code');
            $tmp['asset_status'] = $statusMap[$tmp['status']] ?? null; // 资产状态
            $linkNeedDesc = $entityNoteCodeMap[$tmp['id']] ?? null; // 环节需求
            $tmp['review'] = null;
            if (!empty($linkNeedDesc)) {
                foreach ($linkNeedDesc as $categoryCode => $linkNeedDescItem) {
                    $tmp[$categoryCode . '_link_need_desc'] = $linkNeedDescItem;
                }
            }
            // 环节成果
            $currentEntityIdList = [$tmp['id']];
            if ($tmp['downstream_entity_id'] > 0) {
                $currentEntityIdList[] = $tmp['downstream_entity_id'];
            }
            foreach ($currentEntityIdList as $entityId) {
                if (empty($stepResultMap[$entityId])) {
                    continue;
                }
                $stepCateGoryCodeMap = array_group_by($stepResultMap[$entityId], 'code');
                foreach ($stepCateGoryCodeMap as $code => $stepResultValue) {
                    $stepResultValue = array_sort_by($stepResultValue, 'task_id', 'desc');
                    $latestTask = $stepResultValue[0];
                    $lastTaskId = $latestTask['task_id'];
                    $tmpResult = $versionMap[$lastTaskId] ?? [];
                    $tmpResult['review'] = $reviewList[$lastTaskId] ?? null;
                    if (!empty($tmpResult['review'])) {
                        $tmp['review'] = $tmpResult['review'];
                    }
                    $tmpResult['ent_time'] = $latestTask['end_time'] ?? null;
                    if ($latestTask['task_status'] == $statusCodeMap['final']['id']) {
                        $tmpResult['make_status'] = '当前版本';
                        $tmp[$code . '_link_need_result'] = $tmpResult;
                    } else {
                        $tmp[$code . '_link_need_result'] = ['make_status' => '正在制作', 'ent_time' => $tmpResult['ent_time']];
                    }

                    unset($tmpResult);
                }
            }
            // 资产需求
            $tmp['asset_note'] = $noteAssetMap[$tmp['id']]['text'] ?? null;
            // 剧集id
            $tmp['episode_id'] = $entityEpisodeRelationMap[$tmp['initial_episode_code']]['id'] ?? null;
            // 分镜视频
            $tmp['storyboard_media'] = $storyboardMediaMap[$tmp['storyboard_media']] ?? null;
            // 审核信息
//            $tmp['review'] = $reviewMap[$tmp['id']] ?? null;
            // 任务是否开始
            $tmp['task_is_start'] = !empty($tmp['status']) && in_array($tmp['status'], $taskStartedStatusList);
        }
        return $entity;
    }

    /**
     * 需求列表column
     * @param $moduleCode
     * @param $needColumnCode
     * @return array|array[]
     */
    public function getNoteColumn($moduleCode, $needColumnCode)
    {
        $assetName = '资产';
        if ($moduleCode == 'storyboard') {
            $assetName = '镜头';
        }
        $column = [
            ['index' => 1, 'name' => $assetName . '名称', 'code' => 'asset_name'],
            ['index' => 3, 'name' => '截止时间', 'code' => 'plan_end_time'],
            ['index' => 4, 'name' => $assetName . '状态', 'code' => 'asset_status'],
            ['index' => 5, 'name' => $assetName . '需求', 'code' => 'asset_note'],
            ['index' => 9, 'name' => '备注', 'code' => 'remark'],
        ];
        if ($moduleCode == 'design') {
            $column = array_merge($column, [['index' => 5, 'name' => '使用情况', 'code' => 'use_info']]);
        }
        if ($moduleCode == 'storyboard') {
            $column = array_merge($column, [
                ['index' => 6, 'name' => '关联情况', 'code' => 'relation_info'],
                ['index' => 7, 'name' => '分镜视频', 'code' => 'storyboard_media'],
                ['index' => 2, 'name' => '镜头时长', 'code' => 'storyboard_estimate_duration'], // 镜头预估时长
            ]);
        }

        $needCodeWhere = [];
        if (!empty($needColumnCode)) {
            foreach ($column as $key => $code) {
                if (!in_array($code['code'], $needColumnCode)) {
                    unset($column[$key]);
                }
            }
            $needCodeWhere = ['code' => ['IN', join(',', $needColumnCode)]];
        }

        // 查询展示的环节分类 [资产,概览,关卡,镜头]
        if ($moduleCode == 'design') {
            $designEntityIds = [
                module_by_code('asset')['id'],
                module_by_code('design')['id'],
                module_by_code('level')['id']
            ];
            $stepCategoryDesignIds = model(StepCategoryModel::class)
                ->where([
                    'entity_id' => ['IN', $designEntityIds],
                    'parent_id' => 0,
                ])->column('id');
            $needCodeWhere[] = ['parent_id' => ['IN', $stepCategoryDesignIds]];
        }
        if ($moduleCode == 'storyboard') {
            $storyboardEntityIds = [
                module_by_code('shot')['id'],
            ];
            $stepCategoryDesignIds = model(StepCategoryModel::class)
                ->where([
                    'entity_id' => ['IN', $storyboardEntityIds],
                    'parent_id' => 0,
                ])
                ->column('id');
            $needCodeWhere[] = ['parent_id' => ['IN', $stepCategoryDesignIds]];
        }
        $stepCategoryList = model(StepCategoryModel::class)
            ->where(['is_show' => 'yes'])
            ->where($needCodeWhere)
            ->field("id,code,name,is_show,is_enable_census,show_name,show_index,color")
            ->select();
        $stepCategoryColumn = [];
        foreach ($stepCategoryList as $stepCateItem) {
            $stepCategoryColumn[] = [
                'index' => $stepCateItem['show_index'] + count($column),
                'name' => $stepCateItem['name'],
                'code' => $stepCateItem['code'],
                'step_category_id' => $stepCateItem['id'],
                'group' => [
                    ['code' => 'link_need_desc', 'name' => '环节需求'],
                    ['code' => 'link_need_result', 'name' => '环节成果']
                ]
            ];
        }

        return array_merge($column, $stepCategoryColumn);
    }

    /**
     * 获取环节信息
     * @param $entityList
     * @return array
     */
    public function getStepResultList($entityList)
    {
        $totalEntityIds = [];
        foreach ($entityList as $entity) {
            $totalEntityIds[] = $entity['id'];
            if ($entity['downstream_entity_id'] > 0) {
                $totalEntityIds[] = $entity['downstream_entity_id'];
            }
        }

        return model(EntityModel::class)
            ->join('task on entity.id = task.entity_id')
            ->join('step_category on task.step_category_id = step_category.id')
            ->where(['task.step_type' => 'step', 'entity.id' => ['IN', $totalEntityIds]])
            ->field([
                "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_status')) as task_status",
                'task.id as task_id', 'entity.name', ' entity.module_id', 'task.step_id', 'task.step_category_id', 'task.step_type', 'step_category.code',
                'task.entity_id', 'task.end_time'
            ])
            ->cache(5)
            ->select();
    }

    /**
     * 获取评论用户列表
     * @param $noteIds
     * @return array
     */
    public function getCommentUserList($noteIds)
    {
        $noteFields = ['note.comment_subject_id', 'note.created_by', 'user.id', 'user.avatar', 'user.name', 'user.union_id'];

        // 增加评论人头像
        $createByList = model(NoteModel::class)
            ->join('user on user.id = note.created_by')
            ->where(['comment_subject_id' => ['IN', $noteIds], 'demand_type' => NoteModel::DEMAND_TYPE_COMMENT])
            ->field(build_complex_query_fields($noteFields))
            ->select();

        if (empty($createByList)) {
            return [];
        }

        $noteUserGroup = [];
        foreach ($createByList as $createByIndex => $createByItem) {
            $createByList[$createByIndex] = $createByItem = separate_complex_query_data($createByItem);
            if (!isset($noteUserGroup[$createByItem['note']['comment_subject_id']])) {
                $noteUserGroup[$createByItem['note']['comment_subject_id']] = [$createByItem['user']['id'] => $createByItem['user']];
            } elseif (!in_array($createByItem['user'], $noteUserGroup[$createByItem['note']['comment_subject_id']])) {
                $noteUserGroup[$createByItem['note']['comment_subject_id']][$createByItem['user']['id']] = $createByItem['user'];
            }
        }

        foreach ($noteUserGroup as $noteUserGroupIndex => $noteUserGroupItem) {
            $noteUserGroup[$noteUserGroupIndex] = array_values($noteUserGroupItem);
        }
        return $noteUserGroup;
    }

    /**
     * 获取审核反馈需求结果
     * @param $demandMap
     * @return array
     */
    public function getReviewFeedbackDemandResult($demandMap)
    {
        if (empty($demandMap)) {
            return [];
        }

        $reviewRequestIds = array_values($demandMap);

        $reviewRequestNodeList = model(ReviewRequestNodeModel::class)->where([
            'id' => ["IN", $reviewRequestIds]
        ])->field("id,is_pass")->select();
        $reviewRequestNodeList = array_column($reviewRequestNodeList, null, 'id');

        $result = [];
        foreach ($demandMap as $demandMapIndex => $demandMapItem) {
            if (isset($reviewRequestNodeList[$demandMapItem])) {
                $result[$demandMapIndex] = $reviewRequestNodeList[$demandMapItem];
            }
        }

        return $result;
    }

    /**
     * 月度环节剧集制作排期查询
     * @param $stepCategoryIdList
     * @param $yearMonth
     * @param $projectId
     * @return array|false|mixed|string
     */
    private function getMonthStepCategoryEpisodeList($stepCategoryIdList, $yearMonth, $projectId)
    {
        $start = $yearMonth;
        $end = strtotime("+1 month", $yearMonth) - 1;
        return model(PlanModel::class)
            ->join('task_relation_tag on plan.task_id = task_relation_tag.task_id')
            ->where([
                'plan.project_id' => $projectId,
                'plan.type' => PlanModel::TYPE_STEP_CATEGORY,
                'plan.scope' => PlanModel::SCOPE_SINGLE,
                'plan.end_time' => ['BETWEEN', [$start, $end]],
                'task_relation_tag.link_module_code' => ['IN', ['show_step_category', 'step_category']],
                'task_relation_tag.link_id' => ['IN', $stepCategoryIdList],
            ])
            ->group('plan.entity_id')
            ->field('plan.entity_id')
            ->column('entity_id');
    }

    /**
     * 追加下游实体id
     * @param $entityList
     * @return void
     */
    private function appendEntityUpstreamEntityId(&$entityList)
    {
        if (empty($entityList)) {
            return;
        }
        $currentModuleCode = request()->getModuleCode();
        if ($currentModuleCode == 'storyboard') { // 查环节信息,如果是分镜,storyboard entityId 则转换成 shot entityId
            $shotFilter = [];
            foreach ($entityList as $entity) {
                $shotFilter[] = [
                    'initial_episode_code' => $entity['initial_episode_code'],
                    'showings_number' => $entity['showings_number'],
                    'name' => $entity['name'],
                ];
            }

            $shotFilter['_logic'] = 'OR';
            $shotFilter = [$shotFilter, 'module_id' => module_by_code('shot')['id'], 'project_id' => $entityList[0]['project_id']];
            $shotEntityData = model(EntityModel::class)
                ->where($shotFilter)
                ->field("id,name,project_id,module_id,json ->> '$.shot_storyboard' as upstream_entity_id")
                ->cache(5)
                ->select();
        } else {
            $assetFilter = [
                'entity.module_id' => ['IN', [module_by_code('asset')['id'], module_by_code('level')['id']]],
                'entity.code' => ['IN', implode(',', array_column($entityList, 'code'))]
            ];
            $shotEntityData = model(EntityModel::class)
                ->join('module on module.id = entity.module_id')
                ->where($assetFilter)
                ->field("entity.id,entity.name,entity.project_id,entity.module_id, json_unquote(json_extract(entity.json,concat('$.',module.code,'_design'))) as upstream_entity_id")
                ->cache(5)
                ->select();
        }
        $shotEntityData = array_column($shotEntityData, 'id', 'upstream_entity_id');
        foreach ($entityList as &$entity) {
            $entity['downstream_entity_id'] = $shotEntityData[$entity['id']] ?? 0;
        }
    }

    /**
     * 转换反馈实体关系实体id 为 design storyboard
     * @param $noteEntityRelationList
     * @return array
     */
    public function transformNoteEntityRelationList($noteEntityRelationList)
    {
        $taskIds = [];
        $projectId = 0;
        foreach ($noteEntityRelationList as $noteEntityRelationListItem) {
            if (empty($projectId)) {
                $projectId = $noteEntityRelationListItem["project_id"];
            }
            if ($noteEntityRelationListItem["relation_type"] === "episode") {
                $episodeCodes[$noteEntityRelationListItem["episode_code"]] = $noteEntityRelationListItem["episode_code"];
            } else {
                $entityIds[$noteEntityRelationListItem["entity_id"]] = $noteEntityRelationListItem["entity_id"];

                if ($noteEntityRelationListItem["relation_type"] === "step") {
                    $taskIds[] = $noteEntityRelationListItem["task_id"];
                }
            }
        }
        $taskList = [];
        if (!empty($taskIds)) {
            $taskList = model(TaskModel::class)->where(['id' => ["IN", implode(',', $taskIds)]])->field("id,step_id,step_category_id")->cache(C('database.default_query_cache_time'))->select();
            $taskList = array_column($taskList, null, "id");
        }

        $entityFilter = [];
        if (!empty($entityIds)) {
            $entityFilter = [
                "entity.id" => ["IN", implode(',', $entityIds)]
            ];
        }

        if (!empty($episodeCodes)) {
            if (empty($entityFilter)) {
                $entityFilter = ["entity.code" => ["IN", implode(',', $episodeCodes)], "entity.project_id" => $projectId];
            } else {
                $entityFilter = [
                    $entityFilter,
                    ["entity.code" => ["IN", implode(',', $episodeCodes)], "entity.project_id" => $projectId],
                    "_logic" => "OR"
                ];
            }
        }

        $noteCurrentModuleCode = $data['note']["module_code"] ?? "";

        // 查找entity_id 将资产id替换概念id , 将 镜头id 替换为分镜id
        $entityList = [];
        if (!empty($entityFilter)) {
            $entityList = model(EntityModel::class)
                ->join("module on entity.module_id = module.id")
                ->where($entityFilter)
                ->cache(C('database.default_query_cache_time'))
                ->field(["entity.id", "entity.module_id", "entity.code", "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_design'))) as design",
                    "JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.shot_storyboard')) as shot_storyboard",])
                ->select();
        }


        $entityUpEntityIdMap = [];

        array_map(function ($resultItem) use (&$entityUpEntityIdMap, &$noteCurrentModuleCode) {
            // 判定当前的模块
            if (empty($noteCurrentModuleCode)) {
                if (in_array($resultItem["module_id"], [module_by_code("asset")["id"], module_by_code("level")["id"], module_by_code("design")["id"]])) {
                    $noteCurrentModuleCode = "asset";
                } else {
                    $noteCurrentModuleCode = "shot";
                }
            }
            if (in_array($resultItem["module_id"], [module_by_code("asset")["id"], module_by_code("level")["id"]])) {
                $entityUpEntityIdMap[$resultItem["id"]] = intval($resultItem["design"]);
                return intval($resultItem["design"]);
            } elseif ($resultItem["module_id"] === module_by_code("shot")["id"]) {
                $entityUpEntityIdMap[$resultItem["id"]] = intval($resultItem["shot_storyboard"]);
                return intval($resultItem["shot_storyboard"]);
            } else {
                return $resultItem["id"];
            }
        }, $entityList);

        return array_map(function ($noteEntityRelationItem) use ($entityUpEntityIdMap, $noteCurrentModuleCode, $taskList) {
            if (isset($entityUpEntityIdMap[$noteEntityRelationItem["entity_id"]])) {
                $noteEntityRelationItem["entity_id"] = $entityUpEntityIdMap[$noteEntityRelationItem["entity_id"]];
            }
            if (empty($noteEntityRelationItem["relate_module_code"])) {
                $noteEntityRelationItem["relate_module_code"] = $noteCurrentModuleCode;
            }
            if ($noteEntityRelationItem["relation_type"] === "step") {
                $noteEntityRelationItem["step_id"] = $taskList[$noteEntityRelationItem["task_id"]]["step_id"];
                $noteEntityRelationItem["step_category_id"] = $taskList[$noteEntityRelationItem["task_id"]]["step_category_id"];
            }
            return $noteEntityRelationItem;
        }, $noteEntityRelationList);
    }


    /**
     * 删除需求
     * @param $filter
     * @return array
     */
    public function deleteDemandNote($filter)
    {
        // 获取需求类型
        $demandType = ['feedback', 'review_feedback', 'demand', 'repair_feedback', 'task_progress'];
        $demandList = model(NoteModel::class)
            ->where($filter)
            ->where(["demand_type" => ["IN", $demandType]])
            ->field("id")->select();

        if (empty($demandList)) {
            return [];
        }
        $demandIds = array_column($demandList, "id");
        $noteEntityRelationIds = model(NoteEntityRelationModel::class)->where(['note_id' => ["IN", $demandIds]])->column("id");

        $res = [];
        if (!empty($noteEntityRelationIds)) {
            $res['note_entity_relation'] = model(NoteEntityRelationModel::class)->where(['id' => ["IN", $noteEntityRelationIds]])->delete();
        }
        $res['note'] = model(NoteModel::class)->where(['id' => ["IN", $demandIds]])->delete();
        return $res;
    }


    /**
     * 获取资产关联需求列表
     * @param $param
     * @param $page
     * @param $moduleCode
     * @return array
     */
    public function getAssetRelationNoteList($param, $page, $moduleCode)
    {
        if (!empty($param['project_id'])) {
            $allProjectIds[] = $param['project_id'];  // 外包任务卡片需要看到关联资产信息
            $belongProjectData = model(ProjectModel::class)->where(['id' => $param['project_id']])->field('belong_project_id')->find();
            if (!empty($belongProjectData['belong_project_id'])) {
                $allProjectIds[] = $belongProjectData['belong_project_id'];
            }
            $param['project_id'] = ['IN', $allProjectIds];
        }
        $filter = $this->getNoteManageListFilter($param, $moduleCode); // 需求列表过滤条件处理
        if (empty($filter)) {
            return [];
        }
        if (!empty($param['filter'])) {
            $filter[] = $param['filter'];
        }
        $field = [
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json,concat('$.', module.code, '_grade'))) as grade",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json,concat('$.', module.code, '_category'))) as category",
            "JSON_UNQUOTE(JSON_EXTRACT(entity.json,concat('$.', module.code, '_status'))) as status",
            'entity.initial_episode_code',
            'entity.showings_number',
            'entity.project_id',
            'entity.name as entity_name',
            'entity.id as entity_id',
        ];

        // 类型筛选
        if (!empty($param['category_name'])) {
            $filter[] = [
                "JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_category')))" => ['IN', $param['category_name']]
            ];
        }

        $entityList = model(EntityModel::class)
            ->join('module on entity.module_id = module.id')
            ->join("status on status.id = JSON_UNQUOTE(JSON_EXTRACT(entity.json,concat('$.', module.code, '_status')))", 'LEFT')
            ->where($filter)
            ->field($field)
            ->page($page[0], $page[1])
            ->select();
        if (empty($entityList)) {
            return [];
        }
        $entityIdList = array_column($entityList, 'entity_id');
        $entityMaxNoteIdList = model(NoteEntityRelationModel::class)
            ->where(['entity_id' => ['IN', implode(',', $entityIdList)]])
            ->group('entity_id')
            ->field('entity_id,max(note_id) note_id')->select();
        $noteIdList = array_column($entityMaxNoteIdList, 'note_id');
        $entityMaxNoteIdList = array_column($entityMaxNoteIdList, 'entity_id', 'note_id');

        $noteMap = [];
        if (!empty($noteIdList)) {
            $noteList = model(NoteModel::class)
                ->where(['id' => ['IN', implode(',', $noteIdList)]])
                ->field('id,text')
                ->select();
            foreach ($noteList as $note) {
                if (isset($entityMaxNoteIdList[$note['id']])) {
                    $noteMap[$entityMaxNoteIdList[$note['id']]] = $note['text'];
                }
            }
        }

        $statusIds = array_column($entityList, 'status');
        $statusList = StatusService::getInstance()->getList(['id' => ['IN', $statusIds]], '*');
        $statusMap = array_column($statusList, null, 'id');

        return array_map(function ($tmp) use ($statusMap, $noteMap) {
            $tmp['status'] = $statusMap[$tmp['status']] ?? null;
            $tmp['text'] = $noteMap[$tmp['entity_id']] ?? null;
            return $tmp;
        }, $entityList);
    }

    /**
     * 批量导入需求
     * @param $data
     * @param $projectId
     * @param $type
     * @return array
     * @throws Throwable
     */
    public function importDemand($data, $projectId, $type)
    {

        if (empty($data)) {
            return [
                [
                    'flag' => true, 'err' => "导入需求数据不能为空", 'err_code' => ErrorCode::IMPORT_FEEDBACK_NOTE_FAILURE
                ]
            ];
        }
        $entityGroup = [];
        foreach ($data as $item) {
            $entityGroup[$item['initial_episode_code']][$item['showings_number']][$item['name']] = $item;
        }
        $entityGroupWhere = [];
        foreach ($entityGroup as $initialEpisodeCode => $groupItem) {
            foreach ($groupItem as $showingsNumber => $nameData) {
                foreach ($nameData as $name => $stepCategoryNameList) {
                    $entityGroupWhere[] = [
                        'entity.initial_episode_code' => $initialEpisodeCode,
                        'entity.showings_number' => $showingsNumber,
                        'entity.name' => $name,
                    ];
                }
            }
        }

        $entityGroupWhere['_logic'] = "OR";
        $entityList = model(EntityModel::class)
            ->where(['entity.project_id' => $projectId, 'entity.module_id' => module_by_code($type)['id'], $entityGroupWhere])
            ->field("entity.initial_episode_code,entity.showings_number,entity.code,entity.id,entity.name,json")
            ->select();
        if (empty($entityList)) {
            return [
                [
                    'flag' => true, 'err' => "实体不存在", 'err_code' => ErrorCode::ENTITY_NOT_FOUND
                ]
            ];
        }

        $entityDataList = [];
        foreach ($entityList as $entityItem) {
            $entityDataList[join("_", [$entityItem['initial_episode_code'], $entityItem['showings_number'], $entityItem['name']])] = $entityItem;
        }

        // 查询需求环节
        $stepCategoryIdList = $this->getNeedListStepCategoryList($type);
        // 组织批量导入数据
        $importData = [];
        foreach ($data as $item) {
            $tmpEntityKey = join('_', [$item['initial_episode_code'], $item['showings_number'], $item['name']]);

            if (empty($entityDataList[$tmpEntityKey])) {
                $importData[] = [
                    'flag' => true, 'err' => "{$tmpEntityKey}不存在", 'err_code' => ErrorCode::ENTITY_NOT_FOUND
                ];
                continue;
            }

            if (empty($entityDataList[$tmpEntityKey]['json'])) {
                continue;
            }

            if ($type == "design") {
                $json = json_decode($entityDataList[$tmpEntityKey]['json'], true);
                $currentModuleIds = [module_by_code('design')['id']];
                if ($json['design_category'] == '关卡') {
                    $currentModuleIds[] = module_by_code('level')['id'];
                } else {
                    $currentModuleIds[] = module_by_code('asset')['id'];
                }
            }

            foreach ($stepCategoryIdList as $stepCategoryIdItem) {
                //资产的不能导入关卡的,关卡的不能导入资产
                if ($type == "design" && !in_array($stepCategoryIdItem['module_id'], $currentModuleIds)) {
                    continue;
                }

                $categoryCode = $stepCategoryIdItem['code'];
                if (!isset($item[$categoryCode . '_link_need_desc']) || empty($item[$categoryCode . '_link_need_desc']) || $item[$categoryCode . '_link_need_desc'] === '-') {
                    continue;
                }
                $tmp = [
                    "note" => [
                        "text" => $item[$categoryCode . '_link_need_desc'],
                        "module_code" => $type == 'design' ? 'asset' : 'shot',
                        "project_id" => $projectId,
                        "demand_type" => NoteModel::DEMAND_TYPE_DEMAND,
                    ],
                    "note_entity_relation" => [
                        [
                            "relation_type" => "step_category",
                            "relate_module_code" => $type == 'design' ? 'asset' : 'shot',
                            "project_id" => $projectId,
                            "episode_code" => $item["initial_episode_code"],
                            "entity_id" => $entityDataList[$tmpEntityKey]['id'],
                            "step_category_id" => $stepCategoryIdItem['step_category_id'], // 匹配环节取环节列表环节
                        ]
                    ],
                ];
                $importData[] = $tmp;
            }
            // 资产需求
            if (!empty($item['asset_note'])) {
                $tmp = [
                    "note" => [
                        "text" => $item['asset_note'],
                        "module_code" => $type == 'design' ? 'asset' : 'shot',
                        "project_id" => $projectId,
                        "demand_type" => NoteModel::DEMAND_TYPE_DEMAND,
                        'type' => 'text'
                    ],
                    "note_entity_relation" => [
                        [
                            "relation_type" => "entity",
                            "relate_module_code" => $type == 'design' ? 'asset' : 'shot',
                            "project_id" => $projectId,
                            "episode_code" => $item["initial_episode_code"],
                            "entity_id" => $entityDataList[$tmpEntityKey]['id'],
                        ]
                    ],
                ];
                $importData[] = $tmp;
            }
        }
        $result = [];
        foreach ($importData as $item) {
            if (!empty($item['flag'])) {
                $result[] = $item;
            } else {
                $res = NoteService::getInstance()->createNote($item);
                $result[] = [
                    'flag' => false,
                    'err' => null,
                    'err_code' => 0,
                    'result' => $res['note_entity_relation_list'][0]['entity_id']
                ];
            }
        }


        return $result;
    }


    /**
     * 获取需求环节列表
     * @param $moduleCode
     * @return array
     */
    public function getNeedListStepCategoryList($moduleCode)
    {
        // 查询展示的环节分类 [资产,概览,关卡,镜头]
        $stepCategoryModuleMap = [];
        $moduleIds = [];
        if ($moduleCode == 'design') {
            $moduleIds = [
                module_by_code('asset')['id'],
                module_by_code('design')['id'],
                module_by_code('level')['id']
            ];
        }
        if ($moduleCode == 'storyboard') {
            $moduleIds = [
                module_by_code('shot')['id'],
            ];
        }

        if (empty($moduleIds)) {
            return [];
        }

        $stepCategoryList = model(StepCategoryModel::class)
            ->where([
                'entity_id' => ['IN', $moduleIds],
                'parent_id' => 0,
            ])
            ->field('id,entity_id')
            ->select();

        $stepCategoryIds = [];
        foreach ($stepCategoryList as $stepCategoryItem) {
            $stepCategoryIds[] = $stepCategoryItem['id'];
            $stepCategoryModuleMap[$stepCategoryItem['id']] = $stepCategoryItem['entity_id'];
        }

        $stepCategoryList = model(StepCategoryModel::class)
            ->where(['parent_id' => ['IN', $stepCategoryIds], 'is_show' => 'yes'])
            ->field("id,code,name,is_show,is_enable_census,show_name,show_index,color,parent_id")
            ->select();

        $stepCategoryColumn = [];
        foreach ($stepCategoryList as $stepCateItem) {
            $stepCategoryColumn[] = [
                'name' => $stepCateItem['name'],
                'code' => $stepCateItem['code'],
                'step_category_id' => $stepCateItem['id'],
                'module_id' => $stepCategoryModuleMap[$stepCateItem['parent_id']]
            ];
        }
        return $stepCategoryColumn;
    }
}
